From 3d147249825d2e1b9fc8a595b499fcc24f1b8d6d Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sat, 21 Dec 2024 11:31:32 -0500 Subject: [PATCH 01/34] feat: uart break detection interrupt --- esp-hal/src/uart.rs | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index 0091cdf22b5..fd397b53a09 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -972,6 +972,15 @@ where count } + /// Busy waits for a break condition to be detected on the RX line. + /// Condition is met when the receiver detects a NULL character (i.e. logic 0 for one NULL + /// character transmission) after stop bits. + pub fn wait_for_break(&mut self) { + while !self.register_block().int_raw().read().brk_det().bit_is_set() { + // Just busy waiting + } + } + #[allow(clippy::useless_conversion)] fn rx_fifo_count(&self) -> u16 { let fifo_cnt: u16 = self @@ -1157,6 +1166,11 @@ pub enum UartInterrupt { /// The transmitter has finished sending out all data from the FIFO. TxDone, + /// Break condition has been detected. + /// Triggered when the receiver detects a NULL character (i.e. logic 0 for one NULL + /// character transmission) after stop bits. + RxBreakDetected, + /// The receiver has received more data than what /// [`Config::rx_fifo_full_threshold`] specifies. RxFifoFull, @@ -1613,6 +1627,7 @@ pub(crate) enum TxEvent { pub(crate) enum RxEvent { FifoFull, CmdCharDetected, + BreakDetected, FifoOvf, FifoTout, GlitchDetected, @@ -1651,6 +1666,7 @@ impl UartRxFuture { let event_triggered = match event { RxEvent::FifoFull => interrupts_enabled.rxfifo_full().bit_is_clear(), RxEvent::CmdCharDetected => interrupts_enabled.at_cmd_char_det().bit_is_clear(), + RxEvent::BreakDetected => interrupts_enabled.brk_det().bit_is_clear(), RxEvent::FifoOvf => interrupts_enabled.rxfifo_ovf().bit_is_clear(), RxEvent::FifoTout => interrupts_enabled.rxfifo_tout().bit_is_clear(), @@ -1671,6 +1687,7 @@ impl UartRxFuture { match event { RxEvent::FifoFull => w.rxfifo_full().bit(enable), RxEvent::CmdCharDetected => w.at_cmd_char_det().bit(enable), + RxEvent::BreakDetected => w.brk_det().bit(enable), RxEvent::FifoOvf => w.rxfifo_ovf().bit(enable), RxEvent::FifoTout => w.rxfifo_tout().bit(enable), RxEvent::GlitchDetected => w.glitch_det().bit(enable), @@ -1920,7 +1937,7 @@ where RxEvent::GlitchDetected => return Err(Error::RxGlitchDetected), RxEvent::FrameError => return Err(Error::RxFrameError), RxEvent::ParityError => return Err(Error::RxParityError), - RxEvent::FifoFull | RxEvent::CmdCharDetected | RxEvent::FifoTout => continue, + RxEvent::FifoFull | RxEvent::CmdCharDetected | RxEvent::BreakDetected | RxEvent::FifoTout => continue, } } // Unfortunately, the uart's rx-timeout counter counts up whenever there is @@ -1936,6 +1953,14 @@ where } } } + + /// Interrupt-driven wait for a break condition on the RX line. + /// Condition is met when the receiver detects a NULL character (i.e. logic 0 for one NULL + /// character transmission) after stop bits. + pub async fn wait_for_break_async(&mut self) -> Result<(), Error> { + UartRxFuture::new(self.uart.reborrow(), RxEvent::BreakDetected).await; + Ok(()) + } } #[cfg(any(doc, feature = "unstable"))] @@ -2009,7 +2034,8 @@ pub(super) fn intr_handler(uart: &Info, state: &State) { || 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(); + || interrupts.parity_err().bit_is_set() + || interrupts.brk_det().bit_is_set(); let tx_wake = interrupts.tx_done().bit_is_set() || interrupts.txfifo_empty().bit_is_set(); uart.register_block() .int_clr() @@ -2300,6 +2326,7 @@ impl Info { UartInterrupt::AtCmd => w.at_cmd_char_det().bit(enable), UartInterrupt::TxDone => w.tx_done().bit(enable), UartInterrupt::RxFifoFull => w.rxfifo_full().bit(enable), + UartInterrupt::RxBreakDetected => w.brk_det().bit(enable), }; } w @@ -2321,6 +2348,9 @@ impl Info { if ints.rxfifo_full().bit_is_set() { res.insert(UartInterrupt::RxFifoFull); } + if ints.brk_det().bit_is_set() { + res.insert(UartInterrupt::RxBreakDetected); + } res } @@ -2334,6 +2364,7 @@ impl Info { UartInterrupt::AtCmd => w.at_cmd_char_det().clear_bit_by_one(), UartInterrupt::TxDone => w.tx_done().clear_bit_by_one(), UartInterrupt::RxFifoFull => w.rxfifo_full().clear_bit_by_one(), + UartInterrupt::RxBreakDetected => w.brk_det().clear_bit_by_one(), }; } w From be1a908e3d591e597051352a1eedeeb97a34380b Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sat, 21 Dec 2024 11:33:49 -0500 Subject: [PATCH 02/34] format: run `cargo xtask fmt-packages` --- esp-hal/src/uart.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index fd397b53a09..b3ff41069f6 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -973,10 +973,16 @@ where } /// Busy waits for a break condition to be detected on the RX line. - /// Condition is met when the receiver detects a NULL character (i.e. logic 0 for one NULL - /// character transmission) after stop bits. + /// Condition is met when the receiver detects a NULL character (i.e. logic + /// 0 for one NULL character transmission) after stop bits. pub fn wait_for_break(&mut self) { - while !self.register_block().int_raw().read().brk_det().bit_is_set() { + while !self + .register_block() + .int_raw() + .read() + .brk_det() + .bit_is_set() + { // Just busy waiting } } @@ -1167,8 +1173,8 @@ pub enum UartInterrupt { TxDone, /// Break condition has been detected. - /// Triggered when the receiver detects a NULL character (i.e. logic 0 for one NULL - /// character transmission) after stop bits. + /// Triggered when the receiver detects a NULL character (i.e. logic 0 for + /// one NULL character transmission) after stop bits. RxBreakDetected, /// The receiver has received more data than what @@ -1937,7 +1943,10 @@ where RxEvent::GlitchDetected => return Err(Error::RxGlitchDetected), RxEvent::FrameError => return Err(Error::RxFrameError), RxEvent::ParityError => return Err(Error::RxParityError), - RxEvent::FifoFull | RxEvent::CmdCharDetected | RxEvent::BreakDetected | RxEvent::FifoTout => continue, + RxEvent::FifoFull + | RxEvent::CmdCharDetected + | RxEvent::BreakDetected + | RxEvent::FifoTout => continue, } } // Unfortunately, the uart's rx-timeout counter counts up whenever there is @@ -1955,8 +1964,8 @@ where } /// Interrupt-driven wait for a break condition on the RX line. - /// Condition is met when the receiver detects a NULL character (i.e. logic 0 for one NULL - /// character transmission) after stop bits. + /// Condition is met when the receiver detects a NULL character (i.e. logic + /// 0 for one NULL character transmission) after stop bits. pub async fn wait_for_break_async(&mut self) -> Result<(), Error> { UartRxFuture::new(self.uart.reborrow(), RxEvent::BreakDetected).await; Ok(()) From b7c3f70b308dc4aa3f6b82d2fc4a661c6fd76529 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sat, 21 Dec 2024 11:36:18 -0500 Subject: [PATCH 03/34] docs: CHANGELOG entry --- esp-hal/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 145bb8788d4..f1924e1f51d 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -50,6 +50,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `gpio::{Level, Pull, AlternateFunction, RtcFunction}` now implement `Hash` (#2842) - `gpio::{GpioPin, AnyPin, Io, Output, OutputOpenDrain, Input, Flex}` now implement `Debug`, `defmt::Format` (#2842) - More interrupts are available in `esp_hal::spi::master::SpiInterrupt`, add `enable_listen`,`interrupts` and `clear_interrupts` for ESP32/ESP32-S2 (#2833) +- `uart.wait_for_break()` and `uart.wait_for_break_async().await` (#2858) - The `ExtU64` and `RateExtU32` traits have been added to `esp_hal::time` (#2845) From 3e52d6aea8b54af17d3137172e848474c13909c6 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sat, 21 Dec 2024 11:41:03 -0500 Subject: [PATCH 04/34] fix: no Result needed for `wait_for_break_async` --- esp-hal/src/uart.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index b3ff41069f6..c32d06b7957 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -1966,9 +1966,8 @@ where /// Interrupt-driven wait for a break condition on the RX line. /// Condition is met when the receiver detects a NULL character (i.e. logic /// 0 for one NULL character transmission) after stop bits. - pub async fn wait_for_break_async(&mut self) -> Result<(), Error> { + pub async fn wait_for_break_async(&mut self) { UartRxFuture::new(self.uart.reborrow(), RxEvent::BreakDetected).await; - Ok(()) } } From 44be3e614eafa99ed752ed0671b1ece72c592bea Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sat, 21 Dec 2024 12:09:30 -0500 Subject: [PATCH 05/34] fix: clarify added interrupt in changelog --- esp-hal/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index f1924e1f51d..9ee9ceb57d5 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -50,7 +50,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `gpio::{Level, Pull, AlternateFunction, RtcFunction}` now implement `Hash` (#2842) - `gpio::{GpioPin, AnyPin, Io, Output, OutputOpenDrain, Input, Flex}` now implement `Debug`, `defmt::Format` (#2842) - More interrupts are available in `esp_hal::spi::master::SpiInterrupt`, add `enable_listen`,`interrupts` and `clear_interrupts` for ESP32/ESP32-S2 (#2833) -- `uart.wait_for_break()` and `uart.wait_for_break_async().await` (#2858) +- Additional interrupt available in `esp_hal::uart::UartInterrupt` - as well as UartRx functions `wait_for_break()` and `wait_for_break_async().await` (#2858) - The `ExtU64` and `RateExtU32` traits have been added to `esp_hal::time` (#2845) From d1531ac56d79206464f3dfa5f0ab6b16023a30ce Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sun, 29 Dec 2024 16:57:16 -0500 Subject: [PATCH 06/34] test: adding `uart_interrupts` example --- examples/src/bin/uart_interrupts.rs | 71 +++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 examples/src/bin/uart_interrupts.rs diff --git a/examples/src/bin/uart_interrupts.rs b/examples/src/bin/uart_interrupts.rs new file mode 100644 index 00000000000..22aeff2d094 --- /dev/null +++ b/examples/src/bin/uart_interrupts.rs @@ -0,0 +1,71 @@ +//! Example of responding to UART interrupts. +//! +//! The following wiring is assumed: +//! - TX => GPIO17 +//! - RX => GPIO16 + +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 + +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use critical_section::Mutex; +use esp_backtrace as _; +use esp_hal::{ + delay::Delay, + entry, + interrupt::InterruptConfigurable, + macros::{handler, ram}, + uart::{Config as UartConfig, DataBits, StopBits, Uart, UartInterrupt}, + Blocking, +}; + +static SERIAL: Mutex>>> = Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + let peripherals = esp_hal::init(esp_hal::Config::default()); + let uart_config = UartConfig::default() + .baudrate(19200) + .data_bits(DataBits::DataBits8) + .parity_none() + .stop_bits(StopBits::Stop1) + .rx_fifo_full_threshold(1); + let mut uart = Uart::new( + peripherals.UART1, + uart_config, + peripherals.GPIO17, // TX + peripherals.GPIO16, // RX + ) + .expect("Failed to initialize UART"); + + uart.set_interrupt_handler(handler); + + critical_section::with(|cs| { + uart.listen(UartInterrupt::RxFifoFull | UartInterrupt::RxBreakDetected); + SERIAL.borrow_ref_mut(cs).replace(uart); + }); + + loop {} +} + +#[handler] +#[ram] +fn handler() { + critical_section::with(|cs| { + let mut serial = SERIAL.borrow_ref_mut(cs); + let serial = serial.as_mut().unwrap(); + + if serial.interrupts().contains(UartInterrupt::RxFifoFull) { + esp_println::println!("Byte received: {:?}", serial.read_byte().unwrap()); + } else if serial.interrupts().contains(UartInterrupt::RxBreakDetected) { + esp_println::println!("Break detected"); + } else { + esp_println::println!("Unknown source of interrupt"); + } + + serial.clear_interrupts(UartInterrupt::RxFifoFull | UartInterrupt::RxBreakDetected); + }); +} From a38ed910003a11192d449f68bfb51d504d5cb47f Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sun, 29 Dec 2024 17:30:17 -0500 Subject: [PATCH 07/34] test: adds examples for blocking and async break detection w/o handlers --- esp-hal/src/uart.rs | 17 ++++++++ examples/src/bin/uart_break_detection.rs | 38 ++++++++++++++++++ .../src/bin/uart_break_detection_async.rs | 39 +++++++++++++++++++ examples/src/bin/uart_interrupts.rs | 1 - 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 examples/src/bin/uart_break_detection.rs create mode 100644 examples/src/bin/uart_break_detection_async.rs diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index c32d06b7957..21b6e827335 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -975,6 +975,8 @@ where /// Busy waits for a break condition to be detected on the RX line. /// Condition is met when the receiver detects a NULL character (i.e. logic /// 0 for one NULL character transmission) after stop bits. + /// + /// Clears the break detection interrupt before returning. pub fn wait_for_break(&mut self) { while !self .register_block() @@ -985,6 +987,9 @@ where { // Just busy waiting } + + // Clear the break detection interrupt + self.register_block().int_clr().write(|w| w.brk_det().clear_bit_by_one()); } #[allow(clippy::useless_conversion)] @@ -1277,6 +1282,11 @@ where self.rx.read_byte() } + /// Busy waits for a break condition to be detected on the RX line. + pub fn wait_for_break(&mut self) { + self.rx.wait_for_break(); + } + /// Change the configuration. pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { self.rx.apply_config(config)?; @@ -1829,6 +1839,13 @@ where pub async fn flush_async(&mut self) -> Result<(), Error> { self.tx.flush_async().await } + + /// Asynchronously waits for a break condition to be detected on the RX line. + /// Condition is met when the receiver detects a NULL character (i.e. logic + /// 0 for one NULL character transmission) after stop bits. + pub async fn wait_for_break_async(&mut self) { + self.rx.wait_for_break_async().await; + } } impl UartTx<'_, Async, T> diff --git a/examples/src/bin/uart_break_detection.rs b/examples/src/bin/uart_break_detection.rs new file mode 100644 index 00000000000..27b2e9c0521 --- /dev/null +++ b/examples/src/bin/uart_break_detection.rs @@ -0,0 +1,38 @@ +//! Blocking UART break detection example. +//! +//! The following wiring is assumed: +//! - TX => GPIO17 +//! - RX => GPIO16 + +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: embassy embassy-generic-timers esp-hal/unstable + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use esp_backtrace as _; +use esp_hal::uart::{Config as UartConfig, DataBits, StopBits, Uart, UartInterrupt}; + +#[esp_hal_embassy::main] +async fn main(spawner: Spawner) { + let peripherals = esp_hal::init(esp_hal::Config::default()); + let uart_config = UartConfig::default() + .baudrate(19200) + .data_bits(DataBits::DataBits8) + .parity_none() + .stop_bits(StopBits::Stop1) + .rx_fifo_full_threshold(1); + let mut uart = Uart::new( + peripherals.UART1, + uart_config, + peripherals.GPIO17, // TX + peripherals.GPIO16, // RX + ) + .expect("Failed to initialize UART"); + + loop { + uart.wait_for_break(); + esp_println::println!("Break detected!"); + } +} \ No newline at end of file diff --git a/examples/src/bin/uart_break_detection_async.rs b/examples/src/bin/uart_break_detection_async.rs new file mode 100644 index 00000000000..fc018403f6a --- /dev/null +++ b/examples/src/bin/uart_break_detection_async.rs @@ -0,0 +1,39 @@ +//! Async UART break detection example. +//! +//! The following wiring is assumed: +//! - TX => GPIO17 +//! - RX => GPIO16 + +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: embassy embassy-generic-timers esp-hal/unstable + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use esp_backtrace as _; +use esp_hal::uart::{Config as UartConfig, DataBits, StopBits, Uart}; + +#[esp_hal_embassy::main] +async fn main(_spawner: Spawner) { + let peripherals = esp_hal::init(esp_hal::Config::default()); + let uart_config = UartConfig::default() + .baudrate(19200) + .data_bits(DataBits::DataBits8) + .parity_none() + .stop_bits(StopBits::Stop1) + .rx_fifo_full_threshold(1); + let mut uart = Uart::new( + peripherals.UART1, + uart_config, + peripherals.GPIO17, // TX + peripherals.GPIO16, // RX + ) + .expect("Failed to initialize UART") + .into_async(); + + loop { + uart.wait_for_break_async().await; + esp_println::println!("Break detected!"); + } +} \ No newline at end of file diff --git a/examples/src/bin/uart_interrupts.rs b/examples/src/bin/uart_interrupts.rs index 22aeff2d094..679f49786d0 100644 --- a/examples/src/bin/uart_interrupts.rs +++ b/examples/src/bin/uart_interrupts.rs @@ -14,7 +14,6 @@ use core::cell::RefCell; use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ - delay::Delay, entry, interrupt::InterruptConfigurable, macros::{handler, ram}, From 118dce803f39956d2ad81c62280a2f29dc52ddba Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sun, 29 Dec 2024 17:34:22 -0500 Subject: [PATCH 08/34] chore: format on register write statement --- esp-hal/src/uart.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index 21b6e827335..6cb74620bec 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -975,7 +975,7 @@ where /// Busy waits for a break condition to be detected on the RX line. /// Condition is met when the receiver detects a NULL character (i.e. logic /// 0 for one NULL character transmission) after stop bits. - /// + /// /// Clears the break detection interrupt before returning. pub fn wait_for_break(&mut self) { while !self @@ -989,7 +989,9 @@ where } // Clear the break detection interrupt - self.register_block().int_clr().write(|w| w.brk_det().clear_bit_by_one()); + self.register_block() + .int_clr() + .write(|w| w.brk_det().clear_bit_by_one()); } #[allow(clippy::useless_conversion)] From 735f527f69ddc849e0527ea4e98716733ae76970 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sun, 29 Dec 2024 17:37:26 -0500 Subject: [PATCH 09/34] chore: comment format --- esp-hal/src/uart.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index 6cb74620bec..8e651c83293 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -972,9 +972,9 @@ where count } - /// Busy waits for a break condition to be detected on the RX line. - /// Condition is met when the receiver detects a NULL character (i.e. logic - /// 0 for one NULL character transmission) after stop bits. + /// Busy waits for a break condition to be detected on the RX + /// line. Condition is met when the receiver detects a NULL character + /// (i.e. logic 0 for one NULL character transmission) after stop bits. /// /// Clears the break detection interrupt before returning. pub fn wait_for_break(&mut self) { From fecb91c4db937ad3f0f82c7097051df3a6098dde Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sun, 29 Dec 2024 17:57:20 -0500 Subject: [PATCH 10/34] chore: more formatting --- esp-hal/src/uart.rs | 20 +++++++------------ examples/src/bin/uart_break_detection.rs | 12 ++++++----- .../src/bin/uart_break_detection_async.rs | 2 +- examples/src/bin/uart_interrupts.rs | 9 +++++---- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index 8e651c83293..c7fa4e615bd 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -972,26 +972,20 @@ where count } - /// Busy waits for a break condition to be detected on the RX - /// line. Condition is met when the receiver detects a NULL character + /// Busy waits for a break condition to be detected on the RX + /// line. Condition is met when the receiver detects a NULL character /// (i.e. logic 0 for one NULL character transmission) after stop bits. /// /// Clears the break detection interrupt before returning. pub fn wait_for_break(&mut self) { - while !self - .register_block() - .int_raw() - .read() - .brk_det() - .bit_is_set() - { + while !self.register_block().int_raw().read().brk_det().bit() { // Just busy waiting } // Clear the break detection interrupt self.register_block() .int_clr() - .write(|w| w.brk_det().clear_bit_by_one()); + .write(|w| w.brk_det().bit(true)); } #[allow(clippy::useless_conversion)] @@ -1842,9 +1836,9 @@ where self.tx.flush_async().await } - /// Asynchronously waits for a break condition to be detected on the RX line. - /// Condition is met when the receiver detects a NULL character (i.e. logic - /// 0 for one NULL character transmission) after stop bits. + /// Asynchronously waits for a break condition to be detected on the RX + /// line. Condition is met when the receiver detects a NULL character + /// (i.e. logic 0 for one NULL character transmission) after stop bits. pub async fn wait_for_break_async(&mut self) { self.rx.wait_for_break_async().await; } diff --git a/examples/src/bin/uart_break_detection.rs b/examples/src/bin/uart_break_detection.rs index 27b2e9c0521..cecdebb0079 100644 --- a/examples/src/bin/uart_break_detection.rs +++ b/examples/src/bin/uart_break_detection.rs @@ -10,12 +10,14 @@ #![no_std] #![no_main] -use embassy_executor::Spawner; use esp_backtrace as _; -use esp_hal::uart::{Config as UartConfig, DataBits, StopBits, Uart, UartInterrupt}; +use esp_hal::{ + entry, + uart::{Config as UartConfig, DataBits, StopBits, Uart}, +}; -#[esp_hal_embassy::main] -async fn main(spawner: Spawner) { +#[entry] +fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); let uart_config = UartConfig::default() .baudrate(19200) @@ -35,4 +37,4 @@ async fn main(spawner: Spawner) { uart.wait_for_break(); esp_println::println!("Break detected!"); } -} \ No newline at end of file +} diff --git a/examples/src/bin/uart_break_detection_async.rs b/examples/src/bin/uart_break_detection_async.rs index fc018403f6a..8f5ac5edded 100644 --- a/examples/src/bin/uart_break_detection_async.rs +++ b/examples/src/bin/uart_break_detection_async.rs @@ -36,4 +36,4 @@ async fn main(_spawner: Spawner) { uart.wait_for_break_async().await; esp_println::println!("Break detected!"); } -} \ No newline at end of file +} diff --git a/examples/src/bin/uart_interrupts.rs b/examples/src/bin/uart_interrupts.rs index 679f49786d0..f8e72b18c17 100644 --- a/examples/src/bin/uart_interrupts.rs +++ b/examples/src/bin/uart_interrupts.rs @@ -31,7 +31,7 @@ fn main() -> ! { .data_bits(DataBits::DataBits8) .parity_none() .stop_bits(StopBits::Stop1) - .rx_fifo_full_threshold(1); + .rx_fifo_full_threshold(1); // when more data than this, an interrupt will be triggered let mut uart = Uart::new( peripherals.UART1, uart_config, @@ -43,6 +43,7 @@ fn main() -> ! { uart.set_interrupt_handler(handler); critical_section::with(|cs| { + uart.clear_interrupts(UartInterrupt::RxFifoFull | UartInterrupt::RxBreakDetected); uart.listen(UartInterrupt::RxFifoFull | UartInterrupt::RxBreakDetected); SERIAL.borrow_ref_mut(cs).replace(uart); }); @@ -56,15 +57,15 @@ fn handler() { critical_section::with(|cs| { let mut serial = SERIAL.borrow_ref_mut(cs); let serial = serial.as_mut().unwrap(); - + if serial.interrupts().contains(UartInterrupt::RxFifoFull) { - esp_println::println!("Byte received: {:?}", serial.read_byte().unwrap()); + esp_println::println!("Byte received: {:?}", serial.read_byte()); } else if serial.interrupts().contains(UartInterrupt::RxBreakDetected) { esp_println::println!("Break detected"); } else { esp_println::println!("Unknown source of interrupt"); } - + serial.clear_interrupts(UartInterrupt::RxFifoFull | UartInterrupt::RxBreakDetected); }); } From 473780c36e81edce027d77d270aba190ac34b868 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sun, 29 Dec 2024 18:07:40 -0500 Subject: [PATCH 11/34] fix: for blocking impl be sure to enable the interrupt and clear it --- esp-hal/src/uart.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index c7fa4e615bd..0aa4a5ac98a 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -978,6 +978,11 @@ where /// /// Clears the break detection interrupt before returning. pub fn wait_for_break(&mut self) { + // Enable the break detection interrupt + self.register_block() + .int_ena() + .write(|w| w.brk_det().bit(true)); + while !self.register_block().int_raw().read().brk_det().bit() { // Just busy waiting } From 9d0c1226d9a6c54f94dcb1b04b631ff61ab6f94b Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sun, 29 Dec 2024 18:22:23 -0500 Subject: [PATCH 12/34] fix: flipped rx and tx in `Uart::new()` --- examples/src/bin/uart_break_detection.rs | 3 ++- .../src/bin/uart_break_detection_async.rs | 7 +++++-- examples/src/bin/uart_interrupts.rs | 20 ++++++++++--------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/examples/src/bin/uart_break_detection.rs b/examples/src/bin/uart_break_detection.rs index cecdebb0079..b2847d9c31e 100644 --- a/examples/src/bin/uart_break_detection.rs +++ b/examples/src/bin/uart_break_detection.rs @@ -13,6 +13,7 @@ use esp_backtrace as _; use esp_hal::{ entry, + gpio::{Level, Output}, uart::{Config as UartConfig, DataBits, StopBits, Uart}, }; @@ -28,8 +29,8 @@ fn main() -> ! { let mut uart = Uart::new( peripherals.UART1, uart_config, - peripherals.GPIO17, // TX peripherals.GPIO16, // RX + peripherals.GPIO17, // TX ) .expect("Failed to initialize UART"); diff --git a/examples/src/bin/uart_break_detection_async.rs b/examples/src/bin/uart_break_detection_async.rs index 8f5ac5edded..81737dfadaf 100644 --- a/examples/src/bin/uart_break_detection_async.rs +++ b/examples/src/bin/uart_break_detection_async.rs @@ -12,7 +12,10 @@ use embassy_executor::Spawner; use esp_backtrace as _; -use esp_hal::uart::{Config as UartConfig, DataBits, StopBits, Uart}; +use esp_hal::{ + gpio::{Level, Output}, + uart::{Config as UartConfig, DataBits, StopBits, Uart}, +}; #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { @@ -26,8 +29,8 @@ async fn main(_spawner: Spawner) { let mut uart = Uart::new( peripherals.UART1, uart_config, - peripherals.GPIO17, // TX peripherals.GPIO16, // RX + peripherals.GPIO17, // TX ) .expect("Failed to initialize UART") .into_async(); diff --git a/examples/src/bin/uart_interrupts.rs b/examples/src/bin/uart_interrupts.rs index f8e72b18c17..7207088edaf 100644 --- a/examples/src/bin/uart_interrupts.rs +++ b/examples/src/bin/uart_interrupts.rs @@ -35,16 +35,16 @@ fn main() -> ! { let mut uart = Uart::new( peripherals.UART1, uart_config, - peripherals.GPIO17, // TX peripherals.GPIO16, // RX + peripherals.GPIO17, // TX ) .expect("Failed to initialize UART"); uart.set_interrupt_handler(handler); critical_section::with(|cs| { - uart.clear_interrupts(UartInterrupt::RxFifoFull | UartInterrupt::RxBreakDetected); - uart.listen(UartInterrupt::RxFifoFull | UartInterrupt::RxBreakDetected); + uart.clear_interrupts(UartInterrupt::RxBreakDetected | UartInterrupt::RxFifoFull); + uart.listen(UartInterrupt::RxBreakDetected | UartInterrupt::RxFifoFull); SERIAL.borrow_ref_mut(cs).replace(uart); }); @@ -58,14 +58,16 @@ fn handler() { let mut serial = SERIAL.borrow_ref_mut(cs); let serial = serial.as_mut().unwrap(); - if serial.interrupts().contains(UartInterrupt::RxFifoFull) { - esp_println::println!("Byte received: {:?}", serial.read_byte()); - } else if serial.interrupts().contains(UartInterrupt::RxBreakDetected) { + if serial.interrupts().contains(UartInterrupt::RxBreakDetected) { esp_println::println!("Break detected"); - } else { - esp_println::println!("Unknown source of interrupt"); + + // Clear the RX FIFO + while serial.read_byte().is_ok() {} + } + if serial.interrupts().contains(UartInterrupt::RxFifoFull) { + // esp_println::println!("Byte received: {:?}", serial.read_byte()); } - serial.clear_interrupts(UartInterrupt::RxFifoFull | UartInterrupt::RxBreakDetected); + serial.clear_interrupts(UartInterrupt::RxBreakDetected | UartInterrupt::RxFifoFull); }); } From 06cc4fd8bb6f9d54512aebefe92d3a808f946363 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sun, 29 Dec 2024 18:32:39 -0500 Subject: [PATCH 13/34] fix: note only tested on `esp32` --- examples/src/bin/uart_break_detection.rs | 2 +- examples/src/bin/uart_break_detection_async.rs | 2 +- examples/src/bin/uart_interrupts.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/src/bin/uart_break_detection.rs b/examples/src/bin/uart_break_detection.rs index b2847d9c31e..fafb070c482 100644 --- a/examples/src/bin/uart_break_detection.rs +++ b/examples/src/bin/uart_break_detection.rs @@ -4,7 +4,7 @@ //! - TX => GPIO17 //! - RX => GPIO16 -//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% CHIPS: esp32 //% FEATURES: embassy embassy-generic-timers esp-hal/unstable #![no_std] diff --git a/examples/src/bin/uart_break_detection_async.rs b/examples/src/bin/uart_break_detection_async.rs index 81737dfadaf..cd5d5c7614b 100644 --- a/examples/src/bin/uart_break_detection_async.rs +++ b/examples/src/bin/uart_break_detection_async.rs @@ -4,7 +4,7 @@ //! - TX => GPIO17 //! - RX => GPIO16 -//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% CHIPS: esp32 //% FEATURES: embassy embassy-generic-timers esp-hal/unstable #![no_std] diff --git a/examples/src/bin/uart_interrupts.rs b/examples/src/bin/uart_interrupts.rs index 7207088edaf..b486f1f38c8 100644 --- a/examples/src/bin/uart_interrupts.rs +++ b/examples/src/bin/uart_interrupts.rs @@ -4,7 +4,7 @@ //! - TX => GPIO17 //! - RX => GPIO16 -//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% CHIPS: esp32 #![no_std] #![no_main] From a19f915943789d0096e0c3bb3a056a74c18db00e Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sun, 29 Dec 2024 18:53:26 -0500 Subject: [PATCH 14/34] fix: better handler debug output to demonstrate --- examples/src/bin/uart_interrupts.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/src/bin/uart_interrupts.rs b/examples/src/bin/uart_interrupts.rs index b486f1f38c8..f68fb61440f 100644 --- a/examples/src/bin/uart_interrupts.rs +++ b/examples/src/bin/uart_interrupts.rs @@ -59,13 +59,10 @@ fn handler() { let serial = serial.as_mut().unwrap(); if serial.interrupts().contains(UartInterrupt::RxBreakDetected) { - esp_println::println!("Break detected"); - - // Clear the RX FIFO - while serial.read_byte().is_ok() {} + esp_println::print!("\nBREAK"); } if serial.interrupts().contains(UartInterrupt::RxFifoFull) { - // esp_println::println!("Byte received: {:?}", serial.read_byte()); + esp_println::print!(" {:02X}", serial.read_byte().expect("Failed to read byte")); } serial.clear_interrupts(UartInterrupt::RxBreakDetected | UartInterrupt::RxFifoFull); From 7c830dfd3c411b8cd502116a1e0ad728c344186e Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sun, 29 Dec 2024 19:06:23 -0500 Subject: [PATCH 15/34] fix: again more useful debug output --- examples/src/bin/uart_break_detection.rs | 8 ++++++-- examples/src/bin/uart_break_detection_async.rs | 9 ++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/examples/src/bin/uart_break_detection.rs b/examples/src/bin/uart_break_detection.rs index fafb070c482..d6aa1162937 100644 --- a/examples/src/bin/uart_break_detection.rs +++ b/examples/src/bin/uart_break_detection.rs @@ -36,6 +36,10 @@ fn main() -> ! { loop { uart.wait_for_break(); - esp_println::println!("Break detected!"); + esp_println::print!("\nBREAK"); + + while let Ok(byte) = uart.read_byte() { + esp_println::print!(" {:02X}", byte); + } } -} +} \ No newline at end of file diff --git a/examples/src/bin/uart_break_detection_async.rs b/examples/src/bin/uart_break_detection_async.rs index cd5d5c7614b..02a4503b074 100644 --- a/examples/src/bin/uart_break_detection_async.rs +++ b/examples/src/bin/uart_break_detection_async.rs @@ -37,6 +37,13 @@ async fn main(_spawner: Spawner) { loop { uart.wait_for_break_async().await; - esp_println::println!("Break detected!"); + esp_println::print!("\nBREAK"); + + let mut buf = [0u8; 1024]; + while let Ok(size) = uart.read_async(&mut buf).await { + for i in 0..size { + esp_println::print!(" {:02X}", buf[i]); + } + } } } From af8a2286f2b6847c5f7b1256648f5bd59d882904 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sun, 29 Dec 2024 19:07:20 -0500 Subject: [PATCH 16/34] chore: formatting --- examples/src/bin/uart_break_detection.rs | 4 ++-- examples/src/bin/uart_break_detection_async.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/src/bin/uart_break_detection.rs b/examples/src/bin/uart_break_detection.rs index d6aa1162937..71c92051710 100644 --- a/examples/src/bin/uart_break_detection.rs +++ b/examples/src/bin/uart_break_detection.rs @@ -37,9 +37,9 @@ fn main() -> ! { loop { uart.wait_for_break(); esp_println::print!("\nBREAK"); - + while let Ok(byte) = uart.read_byte() { esp_println::print!(" {:02X}", byte); } } -} \ No newline at end of file +} diff --git a/examples/src/bin/uart_break_detection_async.rs b/examples/src/bin/uart_break_detection_async.rs index 02a4503b074..852a696cfa6 100644 --- a/examples/src/bin/uart_break_detection_async.rs +++ b/examples/src/bin/uart_break_detection_async.rs @@ -38,7 +38,7 @@ async fn main(_spawner: Spawner) { loop { uart.wait_for_break_async().await; esp_println::print!("\nBREAK"); - + let mut buf = [0u8; 1024]; while let Ok(size) = uart.read_async(&mut buf).await { for i in 0..size { From 149c6b9c2aca35d1898adf63c1ae8625b7198499 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sun, 29 Dec 2024 19:18:13 -0500 Subject: [PATCH 17/34] chore: format and debug output --- examples/src/bin/uart_break_detection.rs | 1 - examples/src/bin/uart_break_detection_async.rs | 14 +++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/examples/src/bin/uart_break_detection.rs b/examples/src/bin/uart_break_detection.rs index 71c92051710..68bb3c29ea0 100644 --- a/examples/src/bin/uart_break_detection.rs +++ b/examples/src/bin/uart_break_detection.rs @@ -13,7 +13,6 @@ use esp_backtrace as _; use esp_hal::{ entry, - gpio::{Level, Output}, uart::{Config as UartConfig, DataBits, StopBits, Uart}, }; diff --git a/examples/src/bin/uart_break_detection_async.rs b/examples/src/bin/uart_break_detection_async.rs index 852a696cfa6..d247e464e17 100644 --- a/examples/src/bin/uart_break_detection_async.rs +++ b/examples/src/bin/uart_break_detection_async.rs @@ -12,10 +12,7 @@ use embassy_executor::Spawner; use esp_backtrace as _; -use esp_hal::{ - gpio::{Level, Output}, - uart::{Config as UartConfig, DataBits, StopBits, Uart}, -}; +use esp_hal::uart::{Config as UartConfig, DataBits, StopBits, Uart}; #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { @@ -39,11 +36,10 @@ async fn main(_spawner: Spawner) { uart.wait_for_break_async().await; esp_println::print!("\nBREAK"); - let mut buf = [0u8; 1024]; - while let Ok(size) = uart.read_async(&mut buf).await { - for i in 0..size { - esp_println::print!(" {:02X}", buf[i]); - } + let mut buf = [0u8; 11]; + let len = uart.read_async(&mut buf).await.unwrap(); + for byte in buf.iter().take(len) { + esp_println::print!(" {:02X}", byte); } } } From d344c91eba453a953c2ea39041d45ac8471133da Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sun, 29 Dec 2024 19:45:21 -0500 Subject: [PATCH 18/34] docs: clarify interrupt every time a byte is received --- examples/src/bin/uart_break_detection.rs | 2 +- examples/src/bin/uart_break_detection_async.rs | 2 +- examples/src/bin/uart_interrupts.rs | 9 +++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/examples/src/bin/uart_break_detection.rs b/examples/src/bin/uart_break_detection.rs index 68bb3c29ea0..2085c008884 100644 --- a/examples/src/bin/uart_break_detection.rs +++ b/examples/src/bin/uart_break_detection.rs @@ -24,7 +24,7 @@ fn main() -> ! { .data_bits(DataBits::DataBits8) .parity_none() .stop_bits(StopBits::Stop1) - .rx_fifo_full_threshold(1); + .rx_fifo_full_threshold(1); // interrupt every time a byte is received let mut uart = Uart::new( peripherals.UART1, uart_config, diff --git a/examples/src/bin/uart_break_detection_async.rs b/examples/src/bin/uart_break_detection_async.rs index d247e464e17..b574d530f04 100644 --- a/examples/src/bin/uart_break_detection_async.rs +++ b/examples/src/bin/uart_break_detection_async.rs @@ -22,7 +22,7 @@ async fn main(_spawner: Spawner) { .data_bits(DataBits::DataBits8) .parity_none() .stop_bits(StopBits::Stop1) - .rx_fifo_full_threshold(1); + .rx_fifo_full_threshold(1); // interrupt every time a byte is received let mut uart = Uart::new( peripherals.UART1, uart_config, diff --git a/examples/src/bin/uart_interrupts.rs b/examples/src/bin/uart_interrupts.rs index f68fb61440f..e2fb83ddd6d 100644 --- a/examples/src/bin/uart_interrupts.rs +++ b/examples/src/bin/uart_interrupts.rs @@ -31,7 +31,7 @@ fn main() -> ! { .data_bits(DataBits::DataBits8) .parity_none() .stop_bits(StopBits::Stop1) - .rx_fifo_full_threshold(1); // when more data than this, an interrupt will be triggered + .rx_fifo_full_threshold(1); // interrupt every time a byte is received let mut uart = Uart::new( peripherals.UART1, uart_config, @@ -62,7 +62,12 @@ fn handler() { esp_println::print!("\nBREAK"); } if serial.interrupts().contains(UartInterrupt::RxFifoFull) { - esp_println::print!(" {:02X}", serial.read_byte().expect("Failed to read byte")); + esp_println::print!( + " {:02X}", + serial + .read_byte() + .expect("Failed to read byte but FIFO is full") + ); } serial.clear_interrupts(UartInterrupt::RxBreakDetected | UartInterrupt::RxFifoFull); From a2153e7cee5688b0aa5c5cbeb930bbcdfdd24095 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sun, 29 Dec 2024 19:48:53 -0500 Subject: [PATCH 19/34] fmt: debug message too long --- examples/src/bin/uart_interrupts.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/examples/src/bin/uart_interrupts.rs b/examples/src/bin/uart_interrupts.rs index e2fb83ddd6d..c4e8d7d2e19 100644 --- a/examples/src/bin/uart_interrupts.rs +++ b/examples/src/bin/uart_interrupts.rs @@ -62,12 +62,7 @@ fn handler() { esp_println::print!("\nBREAK"); } if serial.interrupts().contains(UartInterrupt::RxFifoFull) { - esp_println::print!( - " {:02X}", - serial - .read_byte() - .expect("Failed to read byte but FIFO is full") - ); + esp_println::print!(" {:02X}", serial.read_byte().expect("Read byte failed")); } serial.clear_interrupts(UartInterrupt::RxBreakDetected | UartInterrupt::RxFifoFull); From f2c9d35ef9846d0b8ce2e717f5ebec07e3282687 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Mon, 30 Dec 2024 08:55:40 -0500 Subject: [PATCH 20/34] test: adds uart_brk_det HIL test --- hil-test/Cargo.toml | 5 ++++ hil-test/tests/uart_brk_det.rs | 45 ++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 hil-test/tests/uart_brk_det.rs diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 97204555d4e..7a08086d985 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -152,6 +152,11 @@ name = "uart_async" harness = false required-features = ["embassy"] +[[test]] +name = "uart_brk_det" +harness = false +required-features = ["embassy"] + [[test]] name = "uart_regression" harness = false diff --git a/hil-test/tests/uart_brk_det.rs b/hil-test/tests/uart_brk_det.rs new file mode 100644 index 00000000000..3d4c49177ee --- /dev/null +++ b/hil-test/tests/uart_brk_det.rs @@ -0,0 +1,45 @@ +//! UART Break Detection test + +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 + +#![no_std] +#![no_main] + +use esp_hal::{ + uart::{self, Uart}, + Blocking, +}; +use hil_test as _; + +struct Context { + uart: Uart<'static, Blocking>, +} + +#[cfg(test)] +#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] +mod tests { + use super::*; + + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let (_, pin) = hil_test::common_test_pins!(peripherals); + let (rx, tx) = pin.split(); + let uart = Uart::new(peripherals.UART1, uart::Config::default(), rx, tx).unwrap(); + + Context { uart } + } + + #[test] + fn test_wait_for_break_blocking(mut ctx: Context) { + // TODO: Send (or simulate) a break signal, otherwise this will fail via timeout + ctx.uart.wait_for_break(); + } + + #[test] + async fn test_wait_for_break_async(ctx: Context) { + // TODO: Send (or simulate) a break signal, otherwise this will fail via timeout + ctx.uart.into_async().wait_for_break_async().await; + } +} From ed719dad5392816d24b64682e4f6a0fde26028a9 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Mon, 30 Dec 2024 13:41:57 -0500 Subject: [PATCH 21/34] chore: embassy features not needed for blocking example --- examples/src/bin/uart_break_detection.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/src/bin/uart_break_detection.rs b/examples/src/bin/uart_break_detection.rs index 2085c008884..d92c8d90ec7 100644 --- a/examples/src/bin/uart_break_detection.rs +++ b/examples/src/bin/uart_break_detection.rs @@ -5,7 +5,6 @@ //! - RX => GPIO16 //% CHIPS: esp32 -//% FEATURES: embassy embassy-generic-timers esp-hal/unstable #![no_std] #![no_main] From fd52ecf0a49c64549cef0dced5e33abe1f3ef666 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sat, 25 Jan 2025 19:22:52 -0500 Subject: [PATCH 22/34] chore: revert to latest main --- esp-hal/CHANGELOG.md | 92 ++- esp-hal/src/uart.rs | 1474 +++++++++++++++++++----------------------- 2 files changed, 747 insertions(+), 819 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 9ee9ceb57d5..10de537ef2a 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -7,6 +7,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- SPI: Added support for 3-wire SPI (#2919) +- Add separate config for Rx and Tx (UART) #2965 + +### Changed + +- RMT: `TxChannelConfig` and `RxChannelConfig` now support the builder-lite pattern (#2978) +- RMT: Some fields of `TxChannelConfig` and `RxChannelConfig` are now `gpio::Level`-valued instead of `bool` (#2989) +- RMT: The `PulseCode` trait now uses `gpio::Level` to specify output levels instead of `bool` (#2989) +- Uart `write_bytes` and `read_bytes` are now blocking and return the number of bytes written/read (#2882) +- Uart `read_bytes` is now blocking returns the number of bytes read (#2882) +- Uart `flush` is now blocking (#2882) +- Removed `embedded-hal-nb` traits (#2882) +- `timer::wait` is now blocking (#2882) +- By default, set `tx_idle_num` to 0 so that bytes written to TX FIFO are always immediately transmitted. (#2859) +- `Rng` and `Trng` now implement `Peripheral

` (#2992) +- SPI, UART, I2C: `with_` functions of peripheral drivers now disconnect the previously assigned pins from the peripheral. (#3012) +- SPI, UART, I2C: Dropping a driver now disconnects pins from their peripherals. (#3012) + +- `Async` drivers are no longer `Send` (#2980) +- GPIO drivers now take configuration structs, and their constructors are fallible (#2990) +- `flip-link` feature is now a config option +- `flip-link` feature is now a config option (`ESP_HAL_CONFIG_FLIP_LINK`) + +- `flip-link` feature is now a config option (`ESP_HAL_CONFIG_FLIP_LINK`) (#3001) + +- Removed features `psram-quad` and `psram-octal` - replaced by `psram` and the `ESP_HAL_CONFIG_PSRAM_MODE` (`quad`/`octal`) (#3001) + +### Fixed + +- `DmaDescriptor` is now `#[repr(C)]` (#2988) +- Fixed an issue that caused LCD_CAM drivers to turn off their clocks unexpectedly (#3007) +- Fixed an issue where DMA-driver peripherals started transferring before the data was ready (#3003) + +### Removed + +- Removed `Pin`, `RtcPin` and `RtcPinWithResistors` implementations from `Flex` (#2938) + +## [0.23.1] - 2025-01-15 + +### Fixed + +- Fixed `PriorityLock` being ineffective with `Priority::max()` on RISC-V CPUs (#2964) + +## [0.23.0] - 2025-01-15 + ### Added - ESP32-S3: Added SDMMC signals (#2556) @@ -50,13 +96,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `gpio::{Level, Pull, AlternateFunction, RtcFunction}` now implement `Hash` (#2842) - `gpio::{GpioPin, AnyPin, Io, Output, OutputOpenDrain, Input, Flex}` now implement `Debug`, `defmt::Format` (#2842) - More interrupts are available in `esp_hal::spi::master::SpiInterrupt`, add `enable_listen`,`interrupts` and `clear_interrupts` for ESP32/ESP32-S2 (#2833) -- Additional interrupt available in `esp_hal::uart::UartInterrupt` - as well as UartRx functions `wait_for_break()` and `wait_for_break_async().await` (#2858) - - The `ExtU64` and `RateExtU32` traits have been added to `esp_hal::time` (#2845) +- Added `AnyPin::steal(pin_number)` (#2854) +- `adc::{AdcCalSource, Attenuation, Resolution}` now implement `Hash` and `defmt::Format` (#2840) +- `rtc_cntl::{RtcFastClock, RtcSlowClock, RtcCalSel}` now implement `PartialEq`, `Eq`, `Hash` and `defmt::Format` (#2840) +- Added `tsens::TemperatureSensor` peripheral for ESP32C6 and ESP32C3 (#2875) +- Added `with_rx()` and `with_tx()` methods to Uart, UartRx, and UartTx (#2904) +- ESP32-S2: Made Wi-Fi peripheral non virtual. (#2942) +- `UartRx::check_for_errors`, `Uart::check_for_rx_errors`, `{Uart, UartRx}::read_buffered_bytes` (#2935) +- Added `i2c` interrupt API (#2944) ### Changed -- Bump MSRV to 1.83 (#2615) - In addition to taking by value, peripheral drivers can now mutably borrow DMA channel objects. (#2526) - DMA channel objects are no longer wrapped in `Channel`. The `Channel` drivers are now managed by DMA enabled peripheral drivers. (#2526) - The `Dpi` driver and `DpiTransfer` now have a `Mode` type parameter. The driver's asyncness is determined by the asyncness of the `Lcd` used to create it. (#2526) @@ -86,10 +137,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated `esp-pacs` with support for Wi-Fi on the ESP32 and made the peripheral non virtual - `SpiBitOrder`, `SpiDataMode`, `SpiMode` were renamed to `BitOder`, `DataMode` and `Mode` (#2828) - `crate::Mode` was renamed to `crate::DriverMode` (#2828) +- `Spi::with_miso` has been overloaded into `Spi::with_miso` and `Spi::with_sio1` (#2557) - Renamed some I2C error variants (#2844) - I2C: Replaced potential panics with errors. (#2831) - UART: Make `AtCmdConfig` and `ConfigError` non-exhaustive (#2851) - UART: Make `AtCmdConfig` use builder-lite pattern (#2851) +- UART: Fix naming violations for `DataBits`, `Parity`, and `StopBits` enum variants (#2893) +- UART: Remove blocking version of `read_bytes` and rename `drain_fifo` to `read_bytes` instead (#2895) +- Renamed variants of `CpuClock`, made the enum non-exhaustive (#2899) +- SPI: Fix naming violations for `Mode` enum variants (#2902) +- SPI: Fix naming violations for `Address` and `Command` enum variants (#2906) +- `ClockSource` enums are now `#[non_exhaustive]` (#2912) +- `macros` module is now private (#2900) +- `gpio::{Input, Flex}::wakeup_enable` now returns an error instead of panicking. (#2916) +- I2C: Have a dedicated enum to specify the timeout (#2864) +- Removed the `I` prefix from `DriveStrength` enum variants. (#2922) +- Removed the `Attenuation` prefix from `Attenuation` enum variants. (#2922) +- Renamed / changed some I2C error variants (#2844, #2862) +- The `entry` macro is replaced by the `main` macro (#2941) +- `{Uart, UartRx}::read_bytes` now blocks until the buffer is filled. (#2935) +- Bump MSRV to 1.84 (#2951) ### Fixed @@ -98,6 +165,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `spi::master::Spi::{into_async, into_blocking}` are now correctly available on the typed driver, to. (#2674) - It is no longer possible to safely conjure `GpioPin` instances (#2688) - UART: Public API follows `C-WORD_ORDER` Rust API standard (`VerbObject` order) (#2851) +- `DmaRxStreamBuf` now correctly resets the descriptors the next time it's used (#2890) +- i2s: fix pin offset logic for parallel output on i2s1 (#2886) ### Removed @@ -112,8 +181,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed `embedded-hal 0.2.x` impls and deps from `esp-hal` (#2593) - Removed `Camera::set_` functions (#2610) - `DmaTxBuf::{compute_chunk_size, compute_descriptor_count, new_with_block_size}` (#2543) - - The `prelude` module has been removed (#2845) +- SPI: Removed `pub fn read_byte` and `pub fn write_byte` (#2915) +- Removed all peripheral instance type parameters and `new_typed` constructors (#2907) ## [0.22.0] - 2024-11-20 @@ -179,13 +249,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed `get_` prefixes from functions (#2528) - The `Camera` and `I8080` drivers' constructors now only accepts blocking-mode DMA channels. (#2519) - Many peripherals are now disabled by default and also get disabled when the driver is dropped (#2544) +- Updated embassy-time to v0.4 (#2701) + +- Config: Crate prefixes and configuration keys are now separated by `_CONFIG_` (#2848) +- UART: `read_byte` and `write_byte` made private. (#2915) ### Fixed - Fix conflict between `RtcClock::get_xtal_freq` and `Rtc::disable_rom_message_printing` (#2360) - Fixed an issue where interrupts enabled before `esp_hal::init` were disabled. This issue caused the executor created by `#[esp_hal_embassy::main]` to behave incorrectly in multi-core applications. (#2377) - Fixed `TWAI::transmit_async`: bus-off state is not reached when CANH and CANL are shorted. (#2421) -- ESP32: added UART-specific workaround for https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/03-errata-description/esp32/cpu-subsequent-access-halted-when-get-interrupted.html (#2441) +- ESP32: added UART-specific workaround for (#2441) - Fixed some SysTimer race conditions and panics (#2451) - TWAI: accept all messages by default (#2467) - I8080: `set_byte_order()` now works correctly in 16-bit mode (#2487) @@ -223,6 +297,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `parl_io::{no_clk_pin(), NoClkPin}` (#2531) - Removed `get_core` function in favour of `Cpu::current` (#2533) +- Removed `uart::Config` setters and `symbol_length`. (#2847) + ## [0.21.1] ### Fixed @@ -1029,7 +1105,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.1.0] - 2022-08-05 -[Unreleased]: https://github.com/esp-rs/esp-hal/compare/v0.22.0...HEAD +[Unreleased]: https://github.com/esp-rs/esp-hal/compare/v0.23.1...HEAD +[0.23.1]: https://github.com/esp-rs/esp-hal/compare/v0.23.0..v0.23.1 +[0.23.0]: https://github.com/esp-rs/esp-hal/compare/v0.22.0..v0.23.0 [0.22.0]: https://github.com/esp-rs/esp-hal/compare/v0.21.1...v0.22.0 [0.21.1]: https://github.com/esp-rs/esp-hal/compare/v0.21.0...v0.21.1 [0.21.0]: https://github.com/esp-rs/esp-hal/compare/v0.20.1...v0.21.0 @@ -1056,4 +1134,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [0.4.0]: https://github.com/esp-rs/esp-hal/compare/v0.3.0...v0.4.0 [0.3.0]: https://github.com/esp-rs/esp-hal/compare/v0.2.0...v0.3.0 [0.2.0]: https://github.com/esp-rs/esp-hal/compare/v0.1.0...v0.2.0 -[0.1.0]: https://github.com/esp-rs/esp-hal/releases/tag/v0.1.0 +[0.1.0]: https://github.com/esp-rs/esp-hal/releases/tag/v0.1.0 \ No newline at end of file diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index 0aa4a5ac98a..40bd3d4a266 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -20,19 +20,6 @@ //! configured. Additionally, the receive (RX) and transmit (TX) pins need to //! be specified. //! -//! ```rust, no_run -#![doc = crate::before_snippet!()] -//! # use esp_hal::uart::{Config, Uart}; -//! -//! let mut uart1 = Uart::new( -//! peripherals.UART1, -//! Config::default(), -//! peripherals.GPIO1, -//! peripherals.GPIO2, -//! ).unwrap(); -//! # } -//! ``` -//! //! The UART controller can be configured to invert the polarity of the pins. //! This is achieved by inverting the desired pins, and then constructing the //! UART instance using the inverted pins. @@ -49,118 +36,23 @@ //! available. See the examples below for more information on how to interact //! with this driver. //! -//! ## Examples -//! ### Sending and Receiving Data -//! ```rust, no_run -#![doc = crate::before_snippet!()] -//! # use esp_hal::uart::{Config, Uart}; -//! # let mut uart1 = Uart::new( -//! # peripherals.UART1, -//! # Config::default(), -//! # peripherals.GPIO1, -//! # peripherals.GPIO2, -//! # ).unwrap(); -//! // Write bytes out over the UART: -//! uart1.write_bytes(b"Hello, world!").expect("write error!"); -//! # } -//! ``` -//! -//! ### Splitting the UART into RX and TX Components -//! ```rust, no_run -#![doc = crate::before_snippet!()] -//! # use esp_hal::uart::{Config, Uart}; -//! # let mut uart1 = Uart::new( -//! # peripherals.UART1, -//! # Config::default(), -//! # peripherals.GPIO1, -//! # peripherals.GPIO2, -//! # ).unwrap(); -//! // The UART can be split into separate Transmit and Receive components: -//! let (mut rx, mut tx) = uart1.split(); +//! ## Example //! -//! // Each component can be used individually to interact with the UART: -//! tx.write_bytes(&[42u8]).expect("write error!"); -//! let byte = rx.read_byte().expect("read error!"); -//! # } -//! ``` -//! -//! ### Inverting RX and TX Pins -//! ```rust, no_run -#![doc = crate::before_snippet!()] -//! # use esp_hal::uart::{Config, Uart}; -//! -//! let (rx, _) = peripherals.GPIO2.split(); -//! let (_, tx) = peripherals.GPIO1.split(); -//! let mut uart1 = Uart::new( -//! peripherals.UART1, -//! Config::default(), -//! rx.inverted(), -//! tx.inverted(), -//! ).unwrap(); -//! # } -//! ``` -//! -//! ### Constructing RX and TX Components -//! ```rust, no_run -#![doc = crate::before_snippet!()] -//! # use esp_hal::uart::{Config, UartTx, UartRx}; -//! -//! let tx = UartTx::new( -//! peripherals.UART0, -//! Config::default(), -//! peripherals.GPIO1, -//! ).unwrap(); -//! let rx = UartRx::new( -//! peripherals.UART1, -//! Config::default(), -//! peripherals.GPIO2, -//! ).unwrap(); -//! # } -//! ``` -//! -//! ### Operation with interrupts that by UART/Serial +//! ### Handling UART Interrupts //! Notice, that in practice a proper serial terminal should be used //! to connect to the board (espmonitor and espflash won't work) //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::delay::Delay; -//! # use esp_hal::uart::{AtCmdConfig, Config, Uart, UartInterrupt}; -//! let delay = Delay::new(); -//! -//! // Default pins for UART/Serial communication -#![cfg_attr( - esp32, - doc = "let (tx_pin, rx_pin) = (peripherals.GPIO1, peripherals.GPIO3);" -)] -#![cfg_attr( - esp32c2, - doc = "let (tx_pin, rx_pin) = (peripherals.GPIO20, peripherals.GPIO19);" -)] -#![cfg_attr( - esp32c3, - doc = "let (tx_pin, rx_pin) = (peripherals.GPIO21, peripherals.GPIO20);" -)] -#![cfg_attr( - esp32c6, - doc = "let (tx_pin, rx_pin) = (peripherals.GPIO16, peripherals.GPIO17);" -)] -#![cfg_attr( - esp32h2, - doc = "let (tx_pin, rx_pin) = (peripherals.GPIO24, peripherals.GPIO23);" -)] -#![cfg_attr( - any(esp32s2, esp32s3), - doc = "let (tx_pin, rx_pin) = (peripherals.GPIO43, peripherals.GPIO44);" -)] -//! let config = Config::default().rx_fifo_full_threshold(30); -//! -//! let mut uart0 = Uart::new( -//! peripherals.UART0, -//! config, -//! tx_pin, -//! rx_pin -//! ).unwrap(); -//! +//! # use esp_hal::uart::{AtCmdConfig, Config, RxConfig, Uart, UartInterrupt}; +//! # let delay = Delay::new(); +//! # let config = Config::default().with_rx( +//! RxConfig::default().with_fifo_full_threshold(30) +//! ); +//! # let mut uart0 = Uart::new( +//! # peripherals.UART0, +//! # config) +//! # .unwrap(); //! uart0.set_interrupt_handler(interrupt_handler); //! //! critical_section::with(|cs| { @@ -197,11 +89,10 @@ //! let mut serial = SERIAL.borrow_ref_mut(cs); //! let serial = serial.as_mut().unwrap(); //! -//! let mut cnt = 0; -//! while let nb::Result::Ok(_c) = serial.read_byte() { -//! cnt += 1; +//! let mut buf = [0u8; 64]; +//! if let Ok(cnt) = serial.read_buffered_bytes(&mut buf) { +//! writeln!(serial, "Read {} bytes", cnt).ok(); //! } -//! writeln!(serial, "Read {} bytes", cnt).ok(); //! //! let pending_interrupts = serial.interrupts(); //! writeln!( @@ -218,10 +109,10 @@ //! } //! ``` //! -//! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/ -//! [embedded-io]: https://docs.rs/embedded-io/latest/embedded_io/ -//! [embedded-hal-async]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/ -//! [embedded-io-async]: https://docs.rs/embedded-io-async/latest/embedded_io_async/ +//! [embedded-hal]: embedded_hal +//! [embedded-io]: embedded_io +//! [embedded-hal-async]: embedded_hal_async +//! [embedded-io-async]: embedded_io_async use core::{marker::PhantomData, sync::atomic::Ordering, task::Poll}; @@ -234,15 +125,16 @@ use crate::{ asynch::AtomicWaker, clock::Clocks, gpio::{ - interconnect::{PeripheralInput, PeripheralOutput}, + interconnect::{OutputConnection, PeripheralInput, PeripheralOutput}, InputSignal, OutputSignal, + PinGuard, Pull, }, interrupt::{InterruptConfigurable, InterruptHandler}, + pac::uart0::RegisterBlock, peripheral::{Peripheral, PeripheralRef}, - peripherals::{uart0::RegisterBlock, Interrupt}, - private::Internal, + peripherals::Interrupt, system::{PeripheralClockControl, PeripheralGuard}, Async, Blocking, @@ -257,34 +149,29 @@ const CMD_CHAR_DEFAULT: u8 = 0x2b; #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { - /// An invalid configuration argument was provided. + /// The RX FIFO overflow happened. /// - /// This error occurs when an incorrect or invalid argument is passed during - /// the configuration of the UART peripheral. - InvalidArgument, - - /// The RX FIFO overflowed. - RxFifoOvf, + /// This error occurs when RX FIFO is full and a new byte is received. + FifoOverflowed, /// A glitch was detected on the RX line. /// /// This error occurs when an unexpected or erroneous signal (glitch) is /// detected on the UART RX line, which could lead to incorrect data /// reception. - RxGlitchDetected, + GlitchOccurred, /// A framing error was detected on the RX line. /// /// This error occurs when the received data does not conform to the /// expected UART frame format. - RxFrameError, + FrameFormatViolated, /// A parity error was detected on the RX line. /// /// This error occurs when the parity bit in the received data does not /// match the expected parity configuration. - /// with the `async` feature. - RxParityError, + ParityMismatch, } impl core::error::Error for Error {} @@ -292,21 +179,14 @@ impl core::error::Error for Error {} impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - Error::InvalidArgument => write!(f, "An invalid configuration argument was provided"), - Error::RxFifoOvf => write!(f, "The RX FIFO overflowed"), - Error::RxGlitchDetected => write!(f, "A glitch was detected on the RX line"), - Error::RxFrameError => write!(f, "A framing error was detected on the RX line"), - Error::RxParityError => write!(f, "A parity error was detected on the RX line"), + Error::FifoOverflowed => write!(f, "The RX FIFO overflowed"), + Error::GlitchOccurred => write!(f, "A glitch was detected on the RX line"), + Error::FrameFormatViolated => write!(f, "A framing error was detected on the RX line"), + Error::ParityMismatch => write!(f, "A parity error was detected on the RX line"), } } } -impl embedded_hal_nb::serial::Error for Error { - fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { - embedded_hal_nb::serial::ErrorKind::Other - } -} - #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] impl embedded_io::Error for Error { @@ -319,16 +199,15 @@ impl embedded_io::Error for Error { /// UART clock source #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] pub enum ClockSource { - /// APB_CLK clock source (default for UART on all the chips except of - /// esp32c6 and esp32h2) + /// APB_CLK clock source #[cfg_attr(not(any(esp32c6, esp32h2, lp_uart)), default)] Apb, /// RC_FAST_CLK clock source (17.5 MHz) #[cfg(not(any(esp32, esp32s2)))] RcFast, - /// XTAL_CLK clock source (default for UART on esp32c6 and esp32h2 and - /// LP_UART) + /// XTAL_CLK clock source #[cfg(not(any(esp32, esp32s2)))] #[cfg_attr(any(esp32c6, esp32h2, lp_uart), default)] Xtal, @@ -351,14 +230,14 @@ const UART_TOUT_THRESH_DEFAULT: u8 = 10; #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DataBits { /// 5 data bits per frame. - DataBits5 = 0, + _5 = 0, /// 6 data bits per frame. - DataBits6 = 1, + _6 = 1, /// 7 data bits per frame. - DataBits7 = 2, - /// 8 data bits per frame (most common). + _7 = 2, + /// 8 data bits per frame. #[default] - DataBits8 = 3, + _8 = 3, } /// Parity check @@ -370,15 +249,15 @@ pub enum DataBits { #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Parity { - /// No parity bit is used (most common). + /// No parity bit is used. #[default] - ParityNone, + None, /// Even parity: the parity bit is set to make the total number of /// 1-bits even. - ParityEven, + Even, /// Odd parity: the parity bit is set to make the total number of 1-bits /// odd. - ParityOdd, + Odd, } /// Number of stop bits @@ -391,11 +270,11 @@ pub enum Parity { pub enum StopBits { /// 1 stop bit. #[default] - Stop1 = 1, + _1 = 1, /// 1.5 stop bits. - Stop1P5 = 2, + _1p5 = 2, /// 2 stop bits. - Stop2 = 3, + _2 = 3, } /// UART Configuration @@ -414,107 +293,80 @@ pub struct Config { pub stop_bits: StopBits, /// Clock source used by the UART peripheral. pub clock_source: ClockSource, + /// UART Receive part configuration. + pub rx: RxConfig, + /// UART Transmit part configuration. + pub tx: TxConfig, +} + +/// UART Receive part configuration. +#[derive(Debug, Clone, Copy, procmacros::BuilderLite)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct RxConfig { /// Threshold level at which the RX FIFO is considered full. - pub rx_fifo_full_threshold: u16, + pub fifo_full_threshold: u16, /// Optional timeout value for RX operations. - pub rx_timeout: Option, + pub timeout: Option, } -impl Config { - /// Sets the baud rate for the UART configuration. - pub fn baudrate(mut self, baudrate: u32) -> Self { - self.baudrate = baudrate; - self - } - - /// Configures the UART to use no parity check. - pub fn parity_none(mut self) -> Self { - self.parity = Parity::ParityNone; - self - } - - /// Configures the UART to use even parity check. - pub fn parity_even(mut self) -> Self { - self.parity = Parity::ParityEven; - self - } - - /// Configures the UART to use odd parity check. - pub fn parity_odd(mut self) -> Self { - self.parity = Parity::ParityOdd; - self - } - - /// Sets the number of data bits for the UART configuration. - pub fn data_bits(mut self, data_bits: DataBits) -> Self { - self.data_bits = data_bits; - self - } +/// UART Transmit part configuration. +#[derive(Debug, Clone, Copy, Default, procmacros::BuilderLite)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct TxConfig {} - /// Sets the number of stop bits for the UART configuration. - pub fn stop_bits(mut self, stop_bits: StopBits) -> Self { - self.stop_bits = stop_bits; - self +impl Default for RxConfig { + fn default() -> RxConfig { + RxConfig { + fifo_full_threshold: UART_FULL_THRESH_DEFAULT, + timeout: Some(UART_TOUT_THRESH_DEFAULT), + } } +} - /// Sets the clock source for the UART configuration. - pub fn clock_source(mut self, source: ClockSource) -> Self { - self.clock_source = source; - self +impl Default for Config { + fn default() -> Config { + Config { + rx: RxConfig::default(), + tx: TxConfig::default(), + baudrate: 115_200, + data_bits: Default::default(), + parity: Default::default(), + stop_bits: Default::default(), + clock_source: Default::default(), + } } +} +impl Config { /// Calculates the total symbol length in bits based on the configured /// data bits, parity, and stop bits. - pub fn symbol_length(&self) -> u8 { + fn symbol_length(&self) -> u8 { let mut length: u8 = 1; // start bit length += match self.data_bits { - DataBits::DataBits5 => 5, - DataBits::DataBits6 => 6, - DataBits::DataBits7 => 7, - DataBits::DataBits8 => 8, + DataBits::_5 => 5, + DataBits::_6 => 6, + DataBits::_7 => 7, + DataBits::_8 => 8, }; length += match self.parity { - Parity::ParityNone => 0, + Parity::None => 0, _ => 1, }; length += match self.stop_bits { - StopBits::Stop1 => 1, + StopBits::_1 => 1, _ => 2, // esp-idf also counts 2 bits for settings 1.5 and 2 stop bits }; length } - - /// Sets the RX FIFO full threshold for the UART configuration. - pub fn rx_fifo_full_threshold(mut self, threshold: u16) -> Self { - self.rx_fifo_full_threshold = threshold; - self - } - - /// Sets the RX timeout for the UART configuration. - pub fn rx_timeout(mut self, timeout: Option) -> Self { - self.rx_timeout = timeout; - self - } -} - -impl Default for Config { - fn default() -> Config { - Config { - baudrate: 115_200, - data_bits: Default::default(), - parity: Default::default(), - stop_bits: Default::default(), - clock_source: Default::default(), - rx_fifo_full_threshold: UART_FULL_THRESH_DEFAULT, - rx_timeout: Some(UART_TOUT_THRESH_DEFAULT), - } - } } +/// Configuration for the AT-CMD detection functionality #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, procmacros::BuilderLite)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[instability::unstable] #[non_exhaustive] -/// Configuration for the AT-CMD detection functionality pub struct AtCmdConfig { /// Optional idle time before the AT command detection begins, in clock /// cycles. @@ -543,46 +395,30 @@ impl Default for AtCmdConfig { } } -struct UartBuilder<'d, Dm, T = AnyUart> { - uart: PeripheralRef<'d, T>, +struct UartBuilder<'d, Dm> { + uart: PeripheralRef<'d, AnyUart>, phantom: PhantomData, } -impl<'d, Dm, T> UartBuilder<'d, Dm, T> +impl<'d, Dm> UartBuilder<'d, Dm> where - T: Instance, Dm: DriverMode, { - fn new(uart: impl Peripheral

+ 'd) -> Self { - crate::into_ref!(uart); + fn new(uart: impl Peripheral

+ 'd) -> Self { + crate::into_mapped_ref!(uart); Self { uart, phantom: PhantomData, } } - fn with_rx(self, rx: impl Peripheral

+ 'd) -> Self { - crate::into_mapped_ref!(rx); - rx.init_input(Pull::Up, Internal); - self.uart.info().rx_signal.connect_to(rx); - - self - } - - fn with_tx(self, tx: impl Peripheral

+ 'd) -> Self { - crate::into_mapped_ref!(tx); - // Make sure we don't cause an unexpected low pulse on the pin. - tx.set_output_high(true, Internal); - tx.set_to_push_pull_output(Internal); - self.uart.info().tx_signal.connect_to(tx); - - self - } - - fn init(self, config: Config) -> Result, ConfigError> { + fn init(self, config: Config) -> Result, ConfigError> { let rx_guard = PeripheralGuard::new(self.uart.parts().0.peripheral); let tx_guard = PeripheralGuard::new(self.uart.parts().0.peripheral); + let rts_pin = PinGuard::new_unconnected(self.uart.info().rts_signal); + let tx_pin = PinGuard::new_unconnected(self.uart.info().tx_signal); + let mut serial = Uart { rx: UartRx { uart: unsafe { self.uart.clone_unchecked() }, @@ -593,6 +429,8 @@ where uart: self.uart, phantom: PhantomData, guard: tx_guard, + rts_pin, + tx_pin, }, }; serial.init(config)?; @@ -602,21 +440,23 @@ where } /// UART (Full-duplex) -pub struct Uart<'d, Dm, T = AnyUart> { - rx: UartRx<'d, Dm, T>, - tx: UartTx<'d, Dm, T>, +pub struct Uart<'d, Dm> { + rx: UartRx<'d, Dm>, + tx: UartTx<'d, Dm>, } /// UART (Transmit) -pub struct UartTx<'d, Dm, T = AnyUart> { - uart: PeripheralRef<'d, T>, +pub struct UartTx<'d, Dm> { + uart: PeripheralRef<'d, AnyUart>, phantom: PhantomData, guard: PeripheralGuard, + rts_pin: PinGuard, + tx_pin: PinGuard, } /// UART (Receive) -pub struct UartRx<'d, Dm, T = AnyUart> { - uart: PeripheralRef<'d, T>, +pub struct UartRx<'d, Dm> { + uart: PeripheralRef<'d, AnyUart>, phantom: PhantomData, guard: PeripheralGuard, } @@ -647,9 +487,8 @@ impl core::fmt::Display for ConfigError { #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl SetConfig for Uart<'_, Dm, T> +impl SetConfig for Uart<'_, Dm> where - T: Instance, Dm: DriverMode, { type Config = Config; @@ -662,9 +501,8 @@ where #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl SetConfig for UartRx<'_, Dm, T> +impl SetConfig for UartRx<'_, Dm> where - T: Instance, Dm: DriverMode, { type Config = Config; @@ -677,9 +515,8 @@ where #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl SetConfig for UartTx<'_, Dm, T> +impl SetConfig for UartTx<'_, Dm> where - T: Instance, Dm: DriverMode, { type Config = Config; @@ -690,16 +527,31 @@ where } } -impl<'d, Dm, T> UartTx<'d, Dm, T> +impl<'d, Dm> UartTx<'d, Dm> where - T: Instance, Dm: DriverMode, { /// Configure RTS pin - pub fn with_rts(self, rts: impl Peripheral

+ 'd) -> Self { + pub fn with_rts(mut self, rts: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(rts); - rts.set_to_push_pull_output(Internal); - self.uart.info().rts_signal.connect_to(rts); + rts.set_to_push_pull_output(); + self.rts_pin = OutputConnection::connect_with_guard(rts, self.uart.info().rts_signal); + + self + } + + /// Assign the TX pin for UART instance. + /// + /// Sets the specified pin to push-pull output and connects it to the UART + /// TX signal. + /// + /// Disconnects the previous pin that was assigned with `with_tx`. + pub fn with_tx(mut self, tx: impl Peripheral

+ 'd) -> Self { + crate::into_mapped_ref!(tx); + // Make sure we don't cause an unexpected low pulse on the pin. + tx.set_output_high(true); + tx.set_to_push_pull_output(); + self.tx_pin = OutputConnection::connect_with_guard(tx, self.uart.info().tx_signal); self } @@ -707,51 +559,41 @@ where /// Change the configuration. /// /// Note that this also changes the configuration of the RX half. - pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { - self.uart.info().apply_config(config) + #[instability::unstable] + pub fn apply_config(&mut self, _config: &Config) -> Result<(), ConfigError> { + // Nothing to do so far. + self.uart.info().txfifo_reset(); + Ok(()) } /// Writes bytes pub fn write_bytes(&mut self, data: &[u8]) -> Result { let count = data.len(); - data.iter() - .try_for_each(|c| nb::block!(self.write_byte(*c)))?; + for &byte in data { + self.write_byte(byte); + } Ok(count) } - fn write_byte(&mut self, word: u8) -> nb::Result<(), Error> { - if self.tx_fifo_count() < UART_FIFO_SIZE { - self.register_block() - .fifo() - .write(|w| unsafe { w.rxfifo_rd_byte().bits(word) }); - - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } + fn write_byte(&mut self, word: u8) { + while self.tx_fifo_count() >= UART_FIFO_SIZE {} + self.regs() + .fifo() + .write(|w| unsafe { w.rxfifo_rd_byte().bits(word) }); } #[allow(clippy::useless_conversion)] /// Returns the number of bytes currently in the TX FIFO for this UART /// instance. fn tx_fifo_count(&self) -> u16 { - self.register_block() - .status() - .read() - .txfifo_cnt() - .bits() - .into() + self.regs().status().read().txfifo_cnt().bits().into() } /// Flush the transmit buffer of the UART - pub fn flush(&mut self) -> nb::Result<(), Error> { - if self.is_tx_idle() { - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } + pub fn flush(&mut self) { + while !self.is_tx_idle() {} } /// Checks if the TX line is idle for this UART instance. @@ -760,9 +602,9 @@ where /// currently being transmitted. fn is_tx_idle(&self) -> bool { #[cfg(esp32)] - let status = self.register_block().status(); + let status = self.regs().status(); #[cfg(not(esp32))] - let status = self.register_block().fsm_status(); + let status = self.regs().fsm_status(); status.read().st_utx_out().bits() == 0x0 } @@ -773,14 +615,14 @@ where /// `transmit break done`, `transmit break idle done`, and `transmit done` /// interrupts. fn disable_tx_interrupts(&self) { - self.register_block().int_clr().write(|w| { + self.regs().int_clr().write(|w| { w.txfifo_empty().clear_bit_by_one(); w.tx_brk_done().clear_bit_by_one(); w.tx_brk_idle_done().clear_bit_by_one(); w.tx_done().clear_bit_by_one() }); - self.register_block().int_ena().write(|w| { + self.regs().int_ena().write(|w| { w.txfifo_empty().clear_bit(); w.tx_brk_done().clear_bit(); w.tx_brk_idle_done().clear_bit(); @@ -788,42 +630,34 @@ where }); } - fn register_block(&self) -> &RegisterBlock { - self.uart.info().register_block() + fn regs(&self) -> &RegisterBlock { + self.uart.info().regs() } } impl<'d> UartTx<'d, Blocking> { /// Create a new UART TX instance in [`Blocking`] mode. + /// ```rust, no_run + #[doc = crate::before_snippet!()] + /// # use esp_hal::uart::{Config, UartTx}; + /// let tx = UartTx::new( + /// peripherals.UART0, + /// Config::default()) + /// .unwrap() + /// .with_tx(peripherals.GPIO1); + /// # } + /// ``` pub fn new( uart: impl Peripheral

+ 'd, config: Config, - tx: impl Peripheral

+ 'd, ) -> Result { - Self::new_typed(uart.map_into(), config, tx) - } -} - -impl<'d, T> UartTx<'d, Blocking, T> -where - T: Instance, -{ - /// Create a new UART TX instance in [`Blocking`] mode. - pub fn new_typed( - uart: impl Peripheral

+ 'd, - config: Config, - tx: impl Peripheral

+ 'd, - ) -> Result { - let (_, uart_tx) = UartBuilder::<'d, Blocking, T>::new(uart) - .with_tx(tx) - .init(config)? - .split(); + let (_, uart_tx) = UartBuilder::new(uart).init(config)?.split(); Ok(uart_tx) } /// Reconfigures the driver to operate in [`Async`] mode. - pub fn into_async(self) -> UartTx<'d, Async, T> { + pub fn into_async(self) -> UartTx<'d, Async> { if !self.uart.state().is_rx_async.load(Ordering::Acquire) { self.uart .info() @@ -835,16 +669,15 @@ where uart: self.uart, phantom: PhantomData, guard: self.guard, + rts_pin: self.rts_pin, + tx_pin: self.tx_pin, } } } -impl<'d, T> UartTx<'d, Async, T> -where - T: Instance, -{ +impl<'d> UartTx<'d, Async> { /// Reconfigures the driver to operate in [`Blocking`] mode. - pub fn into_blocking(self) -> UartTx<'d, Blocking, T> { + pub fn into_blocking(self) -> UartTx<'d, Blocking> { self.uart .state() .is_tx_async @@ -857,6 +690,8 @@ where uart: self.uart, phantom: PhantomData, guard: self.guard, + rts_pin: self.rts_pin, + tx_pin: self.tx_pin, } } } @@ -881,88 +716,147 @@ fn sync_regs(_register_block: &RegisterBlock) { } } -impl<'d, Dm, T> UartRx<'d, Dm, T> +impl<'d, Dm> UartRx<'d, Dm> where - T: Instance, Dm: DriverMode, { + fn regs(&self) -> &RegisterBlock { + self.uart.info().regs() + } + /// Configure CTS pin pub fn with_cts(self, cts: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(cts); - cts.init_input(Pull::None, Internal); + cts.init_input(Pull::None); self.uart.info().cts_signal.connect_to(cts); self } + /// Assign the RX pin for UART instance. + /// + /// Sets the specified pin to input and connects it to the UART RX signal. + /// + /// Note: when you listen for the output of the UART peripheral, you should + /// configure the driver side (i.e. the TX pin), or ensure that the line is + /// initially high, to avoid receiving a non-data byte caused by an + /// initial low signal level. + pub fn with_rx(self, rx: impl Peripheral

+ 'd) -> Self { + crate::into_mapped_ref!(rx); + rx.init_input(Pull::Up); + self.uart.info().rx_signal.connect_to(rx); + + self + } + /// Change the configuration. /// /// Note that this also changes the configuration of the TX half. + #[instability::unstable] pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { - self.uart.info().apply_config(config) + self.uart + .info() + .set_rx_fifo_full_threshold(config.rx.fifo_full_threshold)?; + self.uart + .info() + .set_rx_timeout(config.rx.timeout, config.symbol_length())?; + + self.uart.info().rxfifo_reset(); + Ok(()) } - /// Fill a buffer with received bytes - pub fn read_bytes(&mut self, buf: &mut [u8]) -> Result<(), Error> { + /// Reads and clears errors. + #[instability::unstable] + pub fn check_for_errors(&mut self) -> Result<(), Error> { + let errors = RxEvent::FifoOvf + | RxEvent::FifoTout + | RxEvent::GlitchDetected + | RxEvent::FrameError + | RxEvent::ParityError; + let events = self.uart.info().rx_events(errors); + let result = rx_event_check_for_error(events); + if result.is_err() { + self.uart.info().clear_rx_events(errors); + } + result + } + + // Read a byte from the UART + fn read_byte(&mut self) -> Option { cfg_if::cfg_if! { if #[cfg(esp32s2)] { // On the ESP32-S2 we need to use PeriBus2 to read the FIFO: let fifo = unsafe { - &*((self.register_block().fifo().as_ptr() as *mut u8).add(0x20C00000) - as *mut crate::peripherals::uart0::FIFO) + &*((self.regs().fifo().as_ptr() as *mut u8).add(0x20C00000) + as *mut crate::pac::uart0::FIFO) }; } else { - let fifo = self.register_block().fifo(); + let fifo = self.regs().fifo(); } } - for byte in buf.iter_mut() { - while self.rx_fifo_count() == 0 { - // Block until we received at least one byte - } - + if self.rx_fifo_count() > 0 { + // https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/03-errata-description/esp32/cpu-subsequent-access-halted-when-get-interrupted.html cfg_if::cfg_if! { if #[cfg(esp32)] { - // https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/03-errata-description/esp32/cpu-subsequent-access-halted-when-get-interrupted.html - crate::interrupt::free(|| { - *byte = fifo.read().rxfifo_rd_byte().bits(); - }); + let byte = crate::interrupt::free(|| fifo.read().rxfifo_rd_byte().bits()); } else { - *byte = fifo.read().rxfifo_rd_byte().bits(); + let byte = fifo.read().rxfifo_rd_byte().bits(); } } + + Some(byte) + } else { + None } + } + + /// Reads bytes from the UART + pub fn read_bytes(&mut self, buf: &mut [u8]) -> Result<(), Error> { + let buffered = self.read_buffered_bytes(buf)?; + let buf = &mut buf[buffered..]; + for byte in buf.iter_mut() { + loop { + if let Some(b) = self.read_byte() { + *byte = b; + break; + } + self.check_for_errors()?; + } + } Ok(()) } - /// Read a byte from the UART - pub fn read_byte(&mut self) -> nb::Result { - cfg_if::cfg_if! { - if #[cfg(esp32s2)] { - // On the ESP32-S2 we need to use PeriBus2 to read the FIFO: - let fifo = unsafe { - &*((self.register_block().fifo().as_ptr() as *mut u8).add(0x20C00000) - as *mut crate::peripherals::uart0::FIFO) - }; + /// Read all available bytes from the RX FIFO into the provided buffer and + /// returns the number of read bytes without blocking. + pub fn read_buffered_bytes(&mut self, buf: &mut [u8]) -> Result { + let mut count = 0; + while count < buf.len() { + if let Some(byte) = self.read_byte() { + buf[count] = byte; + count += 1; } else { - let fifo = self.register_block().fifo(); + break; } } - - if self.rx_fifo_count() > 0 { - Ok(fifo.read().rxfifo_rd_byte().bits()) - } else { - Err(nb::Error::WouldBlock) + if let Err(err) = self.check_for_errors() { + // Drain the buffer. We don't know where the error occurred, so returning + // these bytes would be incorrect. We also don't know if the number of buffered + // bytes fit into the buffer. + // TODO: make this behaviour configurable using UART_ERR_WR_MASK. If the user + // wants to keep the bytes regardless of errors, they should be able to do so. + while self.read_byte().is_some() {} + return Err(err); } + Ok(count) } - /// Read all available bytes from the RX FIFO into the provided buffer and - /// returns the number of read bytes. Never blocks - pub fn drain_fifo(&mut self, buf: &mut [u8]) -> usize { + /// Read bytes from the RX FIFO without checking for errors. + fn flush_buffer(&mut self, buf: &mut [u8]) -> usize { let mut count = 0; while count < buf.len() { - if let Ok(byte) = self.read_byte() { + if let Some(byte) = self.read_byte() { buf[count] = byte; count += 1; } else { @@ -972,43 +866,16 @@ where count } - /// Busy waits for a break condition to be detected on the RX - /// line. Condition is met when the receiver detects a NULL character - /// (i.e. logic 0 for one NULL character transmission) after stop bits. - /// - /// Clears the break detection interrupt before returning. - pub fn wait_for_break(&mut self) { - // Enable the break detection interrupt - self.register_block() - .int_ena() - .write(|w| w.brk_det().bit(true)); - - while !self.register_block().int_raw().read().brk_det().bit() { - // Just busy waiting - } - - // Clear the break detection interrupt - self.register_block() - .int_clr() - .write(|w| w.brk_det().bit(true)); - } - #[allow(clippy::useless_conversion)] fn rx_fifo_count(&self) -> u16 { - let fifo_cnt: u16 = self - .register_block() - .status() - .read() - .rxfifo_cnt() - .bits() - .into(); + let fifo_cnt: u16 = self.regs().status().read().rxfifo_cnt().bits().into(); // Calculate the real count based on the FIFO read and write offset address: // https://www.espressif.com/sites/default/files/documentation/esp32_errata_en.pdf // section 3.17 #[cfg(esp32)] { - let status = self.register_block().mem_rx_status().read(); + let status = self.regs().mem_rx_status().read(); let rd_addr = status.mem_rx_rd_addr().bits(); let wr_addr = status.mem_rx_wr_addr().bits(); @@ -1033,54 +900,45 @@ where /// `receive FIFO overflow`, `receive FIFO timeout`, and `AT command /// character detection` interrupts. fn disable_rx_interrupts(&self) { - self.register_block().int_clr().write(|w| { + self.regs().int_clr().write(|w| { w.rxfifo_full().clear_bit_by_one(); w.rxfifo_ovf().clear_bit_by_one(); w.rxfifo_tout().clear_bit_by_one(); w.at_cmd_char_det().clear_bit_by_one() }); - self.register_block().int_ena().write(|w| { + self.regs().int_ena().write(|w| { w.rxfifo_full().clear_bit(); w.rxfifo_ovf().clear_bit(); w.rxfifo_tout().clear_bit(); w.at_cmd_char_det().clear_bit() }); } - - fn register_block(&self) -> &RegisterBlock { - self.uart.info().register_block() - } } impl<'d> UartRx<'d, Blocking> { /// Create a new UART RX instance in [`Blocking`] mode. + /// ```rust, no_run + #[doc = crate::before_snippet!()] + /// # use esp_hal::uart::{Config, UartRx}; + /// let rx = UartRx::new( + /// peripherals.UART1, + /// Config::default()) + /// .unwrap() + /// .with_rx(peripherals.GPIO2); + /// # } + /// ``` pub fn new( uart: impl Peripheral

+ 'd, config: Config, - rx: impl Peripheral

+ 'd, ) -> Result { - UartRx::new_typed(uart.map_into(), config, rx) - } -} - -impl<'d, T> UartRx<'d, Blocking, T> -where - T: Instance, -{ - /// Create a new UART RX instance in [`Blocking`] mode. - pub fn new_typed( - uart: impl Peripheral

+ 'd, - config: Config, - rx: impl Peripheral

+ 'd, - ) -> Result { - let (uart_rx, _) = UartBuilder::new(uart).with_rx(rx).init(config)?.split(); + let (uart_rx, _) = UartBuilder::new(uart).init(config)?.split(); Ok(uart_rx) } /// Reconfigures the driver to operate in [`Async`] mode. - pub fn into_async(self) -> UartRx<'d, Async, T> { + pub fn into_async(self) -> UartRx<'d, Async> { if !self.uart.state().is_tx_async.load(Ordering::Acquire) { self.uart .info() @@ -1096,12 +954,9 @@ where } } -impl<'d, T> UartRx<'d, Async, T> -where - T: Instance, -{ +impl<'d> UartRx<'d, Async> { /// Reconfigures the driver to operate in [`Blocking`] mode. - pub fn into_blocking(self) -> UartRx<'d, Blocking, T> { + pub fn into_blocking(self) -> UartRx<'d, Blocking> { self.uart .state() .is_rx_async @@ -1120,45 +975,58 @@ where impl<'d> Uart<'d, Blocking> { /// Create a new UART instance in [`Blocking`] mode. + /// ```rust, no_run + #[doc = crate::before_snippet!()] + /// # use esp_hal::uart::{Config, Uart}; + /// let mut uart1 = Uart::new( + /// peripherals.UART1, + /// Config::default()) + /// .unwrap() + /// .with_rx(peripherals.GPIO1) + /// .with_tx(peripherals.GPIO2); + /// # } + /// ``` pub fn new( uart: impl Peripheral

+ 'd, config: Config, - rx: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, - ) -> Result { - Self::new_typed(uart.map_into(), config, rx, tx) - } -} - -impl<'d, T> Uart<'d, Blocking, T> -where - T: Instance, -{ - /// Create a new UART instance in [`Blocking`] mode. - pub fn new_typed( - uart: impl Peripheral

+ 'd, - config: Config, - rx: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, ) -> Result { - UartBuilder::new(uart).with_tx(tx).with_rx(rx).init(config) + UartBuilder::new(uart).init(config) } /// Reconfigures the driver to operate in [`Async`] mode. - pub fn into_async(self) -> Uart<'d, Async, T> { + pub fn into_async(self) -> Uart<'d, Async> { Uart { rx: self.rx.into_async(), tx: self.tx.into_async(), } } + + /// Assign the RX pin for UART instance. + /// + /// Sets the specified pin to input and connects it to the UART RX signal. + /// + /// Note: when you listen for the output of the UART peripheral, you should + /// configure the driver side (i.e. the TX pin), or ensure that the line is + /// initially high, to avoid receiving a non-data byte caused by an + /// initial low signal level. + pub fn with_rx(mut self, rx: impl Peripheral

+ 'd) -> Self { + self.rx = self.rx.with_rx(rx); + self + } + + /// Assign the TX pin for UART instance. + /// + /// Sets the specified pin to push-pull output and connects it to the UART + /// TX signal. + pub fn with_tx(mut self, tx: impl Peripheral

+ 'd) -> Self { + self.tx = self.tx.with_tx(tx); + self + } } -impl<'d, T> Uart<'d, Async, T> -where - T: Instance, -{ +impl<'d> Uart<'d, Async> { /// Reconfigures the driver to operate in [`Blocking`] mode. - pub fn into_blocking(self) -> Uart<'d, Blocking, T> { + pub fn into_blocking(self) -> Uart<'d, Blocking> { Uart { rx: self.rx.into_blocking(), tx: self.tx.into_blocking(), @@ -1170,6 +1038,7 @@ where #[derive(Debug, EnumSetType)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] +#[instability::unstable] pub enum UartInterrupt { /// Indicates that the received has detected the configured /// [`Uart::set_at_cmd`] character. @@ -1178,19 +1047,13 @@ pub enum UartInterrupt { /// The transmitter has finished sending out all data from the FIFO. TxDone, - /// Break condition has been detected. - /// Triggered when the receiver detects a NULL character (i.e. logic 0 for - /// one NULL character transmission) after stop bits. - RxBreakDetected, - /// The receiver has received more data than what - /// [`Config::rx_fifo_full_threshold`] specifies. + /// [`RxConfig::fifo_full_threshold`] specifies. RxFifoFull, } -impl<'d, Dm, T> Uart<'d, Dm, T> +impl<'d, Dm> Uart<'d, Dm> where - T: Instance, Dm: DriverMode, { /// Configure CTS pin @@ -1205,92 +1068,118 @@ where self } - fn register_block(&self) -> &RegisterBlock { + fn regs(&self) -> &RegisterBlock { // `self.tx.uart` and `self.rx.uart` are the same - self.tx.uart.info().register_block() + self.tx.uart.info().regs() } /// Split the UART into a transmitter and receiver /// /// This is particularly useful when having two tasks correlating to /// transmitting and receiving. - pub fn split(self) -> (UartRx<'d, Dm, T>, UartTx<'d, Dm, T>) { + /// ## Example + /// ```rust, no_run + #[doc = crate::before_snippet!()] + /// # use esp_hal::uart::{Config, Uart}; + /// # let mut uart1 = Uart::new( + /// # peripherals.UART1, + /// # Config::default()) + /// # .unwrap() + /// # .with_rx(peripherals.GPIO1) + /// # .with_tx(peripherals.GPIO2); + /// // The UART can be split into separate Transmit and Receive components: + /// let (mut rx, mut tx) = uart1.split(); + /// + /// // Each component can be used individually to interact with the UART: + /// tx.write_bytes(&[42u8]).expect("write error!"); + /// let mut byte = [0u8; 1]; + /// rx.read_bytes(&mut byte); + /// # } + /// ``` + pub fn split(self) -> (UartRx<'d, Dm>, UartTx<'d, Dm>) { (self.rx, self.tx) } /// Write bytes out over the UART + /// ```rust, no_run + #[doc = crate::before_snippet!()] + /// # use esp_hal::uart::{Config, Uart}; + /// # let mut uart1 = Uart::new( + /// # peripherals.UART1, + /// # Config::default()) + /// # .unwrap(); + /// // Write bytes out over the UART: + /// uart1.write_bytes(b"Hello, world!").expect("write error!"); + /// # } + /// ``` pub fn write_bytes(&mut self, data: &[u8]) -> Result { self.tx.write_bytes(data) } - /// Fill a buffer with received bytes + /// Reads and clears errors set by received data. + #[instability::unstable] + pub fn check_for_rx_errors(&mut self) -> Result<(), Error> { + self.rx.check_for_errors() + } + + /// Reads bytes from the UART pub fn read_bytes(&mut self, buf: &mut [u8]) -> Result<(), Error> { self.rx.read_bytes(buf) } + /// Read all available bytes from the RX FIFO into the provided buffer and + /// returns the number of read bytes without blocking. + pub fn read_buffered_bytes(&mut self, buf: &mut [u8]) -> Result { + self.rx.read_buffered_bytes(buf) + } + /// Configures the AT-CMD detection settings + #[instability::unstable] pub fn set_at_cmd(&mut self, config: AtCmdConfig) { - let register_block = self.register_block(); - #[cfg(not(any(esp32, esp32s2)))] - register_block + self.regs() .clk_conf() .modify(|_, w| w.sclk_en().clear_bit()); - register_block.at_cmd_char().write(|w| unsafe { + self.regs().at_cmd_char().write(|w| unsafe { w.at_cmd_char().bits(config.cmd_char); w.char_num().bits(config.char_num) }); if let Some(pre_idle_count) = config.pre_idle_count { - register_block + self.regs() .at_cmd_precnt() .write(|w| unsafe { w.pre_idle_num().bits(pre_idle_count as _) }); } if let Some(post_idle_count) = config.post_idle_count { - register_block + self.regs() .at_cmd_postcnt() .write(|w| unsafe { w.post_idle_num().bits(post_idle_count as _) }); } if let Some(gap_timeout) = config.gap_timeout { - register_block + self.regs() .at_cmd_gaptout() .write(|w| unsafe { w.rx_gap_tout().bits(gap_timeout as _) }); } #[cfg(not(any(esp32, esp32s2)))] - register_block - .clk_conf() - .modify(|_, w| w.sclk_en().set_bit()); + self.regs().clk_conf().modify(|_, w| w.sclk_en().set_bit()); - sync_regs(register_block); - } - - /// Write a byte out over the UART - pub fn write_byte(&mut self, word: u8) -> nb::Result<(), Error> { - self.tx.write_byte(word) + sync_regs(self.regs()); } /// Flush the transmit buffer of the UART - pub fn flush(&mut self) -> nb::Result<(), Error> { + pub fn flush(&mut self) { self.tx.flush() } - /// Read a byte from the UART - pub fn read_byte(&mut self) -> nb::Result { - self.rx.read_byte() - } - - /// Busy waits for a break condition to be detected on the RX line. - pub fn wait_for_break(&mut self) { - self.rx.wait_for_break(); - } - /// Change the configuration. pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { self.rx.apply_config(config)?; + self.tx.apply_config(config)?; + self.rx.uart.info().apply_config(config)?; Ok(()) } @@ -1300,11 +1189,11 @@ where if #[cfg(any(esp32, esp32s2))] { // Nothing to do } else if #[cfg(any(esp32c2, esp32c3, esp32s3))] { - unsafe { crate::peripherals::SYSTEM::steal() } + crate::peripherals::SYSTEM::regs() .perip_clk_en0() .modify(|_, w| w.uart_mem_clk_en().set_bit()); } else { - self.register_block() + self.regs() .conf0() .modify(|_, w| w.mem_clk_en().set_bit()); } @@ -1315,21 +1204,23 @@ where self.rx.disable_rx_interrupts(); self.tx.disable_tx_interrupts(); - self.rx.uart.info().apply_config(&config)?; + self.apply_config(&config)?; + + // Don't wait after transmissions by default, + // so that bytes written to TX FIFO are always immediately transmitted. + self.regs() + .idle_conf() + .modify(|_, w| unsafe { w.tx_idle_num().bits(0) }); // Setting err_wr_mask stops uart from storing data when data is wrong according // to reference manual - self.register_block() - .conf0() - .modify(|_, w| w.err_wr_mask().set_bit()); + self.regs().conf0().modify(|_, w| w.err_wr_mask().set_bit()); crate::rom::ets_delay_us(15); // Make sure we are starting in a "clean state" - previous operations might have // run into error conditions - self.register_block() - .int_clr() - .write(|w| unsafe { w.bits(u32::MAX) }); + self.regs().int_clr().write(|w| unsafe { w.bits(u32::MAX) }); Ok(()) } @@ -1361,52 +1252,69 @@ where .modify(|_, w| w.rst_core().bit(_enable)); } - rst_core(self.register_block(), true); + rst_core(self.regs(), true); PeripheralClockControl::reset(self.tx.uart.info().peripheral); - rst_core(self.register_block(), false); + rst_core(self.regs(), false); } } -impl crate::private::Sealed for Uart<'_, Blocking, T> where T: Instance {} +impl crate::private::Sealed for Uart<'_, Blocking> {} -impl InterruptConfigurable for Uart<'_, Blocking, T> -where - T: Instance, -{ - fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { +impl InterruptConfigurable for Uart<'_, Blocking> { + fn set_interrupt_handler(&mut self, handler: InterruptHandler) { // `self.tx.uart` and `self.rx.uart` are the same self.tx.uart.info().set_interrupt_handler(handler); } } -impl Uart<'_, Blocking, T> -where - T: Instance, -{ +impl Uart<'_, Blocking> { + #[cfg_attr( + not(multi_core), + doc = "Registers an interrupt handler for the peripheral." + )] + #[cfg_attr( + multi_core, + doc = "Registers an interrupt handler for the peripheral on the current core." + )] + #[doc = ""] + /// Note that this will replace any previously registered interrupt + /// handlers. + /// + /// You can restore the default/unhandled interrupt handler by using + /// [crate::interrupt::DEFAULT_INTERRUPT_HANDLER] + #[instability::unstable] + pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) { + // `self.tx.uart` and `self.rx.uart` are the same + self.tx.uart.info().set_interrupt_handler(handler); + } + /// Listen for the given interrupts + #[instability::unstable] pub fn listen(&mut self, interrupts: impl Into>) { self.tx.uart.info().enable_listen(interrupts.into(), true) } /// Unlisten the given interrupts + #[instability::unstable] pub fn unlisten(&mut self, interrupts: impl Into>) { self.tx.uart.info().enable_listen(interrupts.into(), false) } /// Gets asserted interrupts + #[instability::unstable] pub fn interrupts(&mut self) -> EnumSet { self.tx.uart.info().interrupts() } /// Resets asserted interrupts + #[instability::unstable] pub fn clear_interrupts(&mut self, interrupts: EnumSet) { self.tx.uart.info().clear_interrupts(interrupts) } } -impl ufmt_write::uWrite for Uart<'_, Dm, T> +impl ufmt_write::uWrite for Uart<'_, Dm> where - T: Instance, Dm: DriverMode, { type Error = Error; @@ -1422,9 +1330,8 @@ where } } -impl ufmt_write::uWrite for UartTx<'_, Dm, T> +impl ufmt_write::uWrite for UartTx<'_, Dm> where - T: Instance, Dm: DriverMode, { type Error = Error; @@ -1436,9 +1343,8 @@ where } } -impl core::fmt::Write for Uart<'_, Dm, T> +impl core::fmt::Write for Uart<'_, Dm> where - T: Instance, Dm: DriverMode, { #[inline] @@ -1447,9 +1353,8 @@ where } } -impl core::fmt::Write for UartTx<'_, Dm, T> +impl core::fmt::Write for UartTx<'_, Dm> where - T: Instance, Dm: DriverMode, { #[inline] @@ -1460,89 +1365,28 @@ where } } -impl embedded_hal_nb::serial::ErrorType for Uart<'_, Dm, T> { - type Error = Error; -} - -impl embedded_hal_nb::serial::ErrorType for UartTx<'_, Dm, T> { - type Error = Error; -} - -impl embedded_hal_nb::serial::ErrorType for UartRx<'_, Dm, T> { - type Error = Error; -} - -impl embedded_hal_nb::serial::Read for Uart<'_, Dm, T> -where - T: Instance, - Dm: DriverMode, -{ - fn read(&mut self) -> nb::Result { - self.read_byte() - } -} - -impl embedded_hal_nb::serial::Read for UartRx<'_, Dm, T> -where - T: Instance, - Dm: DriverMode, -{ - fn read(&mut self) -> nb::Result { - self.read_byte() - } -} - -impl embedded_hal_nb::serial::Write for Uart<'_, Dm, T> -where - T: Instance, - Dm: DriverMode, -{ - fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { - self.write_byte(word) - } - - fn flush(&mut self) -> nb::Result<(), Self::Error> { - self.flush() - } -} - -impl embedded_hal_nb::serial::Write for UartTx<'_, Dm, T> -where - T: Instance, - Dm: DriverMode, -{ - fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { - self.write_byte(word) - } - - fn flush(&mut self) -> nb::Result<(), Self::Error> { - self.flush() - } -} - #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::ErrorType for Uart<'_, Dm, T> { +impl embedded_io::ErrorType for Uart<'_, Dm> { type Error = Error; } #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::ErrorType for UartTx<'_, Dm, T> { +impl embedded_io::ErrorType for UartTx<'_, Dm> { type Error = Error; } #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::ErrorType for UartRx<'_, Dm, T> { +impl embedded_io::ErrorType for UartRx<'_, Dm> { type Error = Error; } #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::Read for Uart<'_, Dm, T> +impl embedded_io::Read for Uart<'_, Dm> where - T: Instance, Dm: DriverMode, { fn read(&mut self, buf: &mut [u8]) -> Result { @@ -1552,9 +1396,8 @@ where #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::Read for UartRx<'_, Dm, T> +impl embedded_io::Read for UartRx<'_, Dm> where - T: Instance, Dm: DriverMode, { fn read(&mut self, buf: &mut [u8]) -> Result { @@ -1566,15 +1409,14 @@ where // Block until we received at least one byte } - Ok(self.drain_fifo(buf)) + self.read_buffered_bytes(buf) } } #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::ReadReady for Uart<'_, Dm, T> +impl embedded_io::ReadReady for Uart<'_, Dm> where - T: Instance, Dm: DriverMode, { fn read_ready(&mut self) -> Result { @@ -1584,9 +1426,8 @@ where #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::ReadReady for UartRx<'_, Dm, T> +impl embedded_io::ReadReady for UartRx<'_, Dm> where - T: Instance, Dm: DriverMode, { fn read_ready(&mut self) -> Result { @@ -1596,9 +1437,8 @@ where #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::Write for Uart<'_, Dm, T> +impl embedded_io::Write for Uart<'_, Dm> where - T: Instance, Dm: DriverMode, { fn write(&mut self, buf: &[u8]) -> Result { @@ -1612,9 +1452,8 @@ where #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::Write for UartTx<'_, Dm, T> +impl embedded_io::Write for UartTx<'_, Dm> where - T: Instance, Dm: DriverMode, { fn write(&mut self, buf: &[u8]) -> Result { @@ -1622,29 +1461,21 @@ where } fn flush(&mut self) -> Result<(), Self::Error> { - loop { - match self.flush() { - Ok(_) => break, - Err(nb::Error::WouldBlock) => { /* Wait */ } - Err(nb::Error::Other(e)) => return Err(e), - } - } - + self.flush(); Ok(()) } } #[derive(Debug, EnumSetType)] pub(crate) enum TxEvent { - TxDone, - TxFiFoEmpty, + Done, + FiFoEmpty, } #[derive(Debug, EnumSetType)] pub(crate) enum RxEvent { FifoFull, CmdCharDetected, - BreakDetected, FifoOvf, FifoTout, GlitchDetected, @@ -1652,6 +1483,20 @@ pub(crate) enum RxEvent { ParityError, } +fn rx_event_check_for_error(events: EnumSet) -> Result<(), Error> { + for event in events { + match event { + RxEvent::FifoOvf => return Err(Error::FifoOverflowed), + RxEvent::GlitchDetected => return Err(Error::GlitchOccurred), + RxEvent::FrameError => return Err(Error::FrameFormatViolated), + RxEvent::ParityError => return Err(Error::ParityMismatch), + RxEvent::FifoFull | RxEvent::CmdCharDetected | RxEvent::FifoTout => continue, + } + } + + Ok(()) +} + /// A future that resolves when the passed interrupt is triggered, /// or has been triggered in the meantime (flag set in INT_RAW). /// Upon construction the future enables the passed interrupt and when it @@ -1675,46 +1520,6 @@ impl UartRxFuture { registered: false, } } - - fn triggered_events(&self) -> EnumSet { - let interrupts_enabled = self.uart.register_block().int_ena().read(); - let mut events_triggered = EnumSet::new(); - for event in self.events { - let event_triggered = match event { - RxEvent::FifoFull => interrupts_enabled.rxfifo_full().bit_is_clear(), - RxEvent::CmdCharDetected => interrupts_enabled.at_cmd_char_det().bit_is_clear(), - RxEvent::BreakDetected => interrupts_enabled.brk_det().bit_is_clear(), - - RxEvent::FifoOvf => interrupts_enabled.rxfifo_ovf().bit_is_clear(), - RxEvent::FifoTout => interrupts_enabled.rxfifo_tout().bit_is_clear(), - RxEvent::GlitchDetected => interrupts_enabled.glitch_det().bit_is_clear(), - RxEvent::FrameError => interrupts_enabled.frm_err().bit_is_clear(), - RxEvent::ParityError => interrupts_enabled.parity_err().bit_is_clear(), - }; - if event_triggered { - events_triggered |= event; - } - } - events_triggered - } - - fn enable_listen(&self, enable: bool) { - self.uart.register_block().int_ena().modify(|_, w| { - for event in self.events { - match event { - RxEvent::FifoFull => w.rxfifo_full().bit(enable), - RxEvent::CmdCharDetected => w.at_cmd_char_det().bit(enable), - RxEvent::BreakDetected => w.brk_det().bit(enable), - RxEvent::FifoOvf => w.rxfifo_ovf().bit(enable), - RxEvent::FifoTout => w.rxfifo_tout().bit(enable), - RxEvent::GlitchDetected => w.glitch_det().bit(enable), - RxEvent::FrameError => w.frm_err().bit(enable), - RxEvent::ParityError => w.parity_err().bit(enable), - }; - } - w - }); - } } impl core::future::Future for UartRxFuture { @@ -1726,10 +1531,10 @@ impl core::future::Future for UartRxFuture { ) -> core::task::Poll { if !self.registered { self.state.rx_waker.register(cx.waker()); - self.enable_listen(true); + self.uart.enable_listen_rx(self.events, true); self.registered = true; } - let events = self.triggered_events(); + let events = self.uart.enabled_rx_events(self.events); if !events.is_empty() { Poll::Ready(events) } else { @@ -1743,7 +1548,7 @@ impl Drop for UartRxFuture { // Although the isr disables the interrupt that occurred directly, we need to // disable the other interrupts (= the ones that did not occur), as // soon as this future goes out of scope. - self.enable_listen(false); + self.uart.enable_listen_rx(self.events, false); } } @@ -1767,23 +1572,23 @@ impl UartTxFuture { } fn triggered_events(&self) -> bool { - let interrupts_enabled = self.uart.register_block().int_ena().read(); + let interrupts_enabled = self.uart.regs().int_ena().read(); let mut event_triggered = false; for event in self.events { event_triggered |= match event { - TxEvent::TxDone => interrupts_enabled.tx_done().bit_is_clear(), - TxEvent::TxFiFoEmpty => interrupts_enabled.txfifo_empty().bit_is_clear(), + TxEvent::Done => interrupts_enabled.tx_done().bit_is_clear(), + TxEvent::FiFoEmpty => interrupts_enabled.txfifo_empty().bit_is_clear(), } } event_triggered } fn enable_listen(&self, enable: bool) { - self.uart.register_block().int_ena().modify(|_, w| { + self.uart.regs().int_ena().modify(|_, w| { for event in self.events { match event { - TxEvent::TxDone => w.tx_done().bit(enable), - TxEvent::TxFiFoEmpty => w.txfifo_empty().bit(enable), + TxEvent::Done => w.tx_done().bit(enable), + TxEvent::FiFoEmpty => w.txfifo_empty().bit(enable), }; } w @@ -1821,10 +1626,7 @@ impl Drop for UartTxFuture { } } -impl Uart<'_, Async, T> -where - T: Instance, -{ +impl Uart<'_, Async> { /// Asynchronously reads data from the UART receive buffer into the /// provided buffer. pub async fn read_async(&mut self, buf: &mut [u8]) -> Result { @@ -1840,19 +1642,9 @@ where pub async fn flush_async(&mut self) -> Result<(), Error> { self.tx.flush_async().await } - - /// Asynchronously waits for a break condition to be detected on the RX - /// line. Condition is met when the receiver detects a NULL character - /// (i.e. logic 0 for one NULL character transmission) after stop bits. - pub async fn wait_for_break_async(&mut self) { - self.rx.wait_for_break_async().await; - } } -impl UartTx<'_, Async, T> -where - T: Instance, -{ +impl UartTx<'_, Async> { /// Asynchronously writes data to the UART transmit buffer in chunks. /// /// This function sends the contents of the provided buffer `words` over @@ -1869,7 +1661,7 @@ where } for byte in &words[offset..next_offset] { - self.write_byte(*byte).unwrap(); // should never fail + self.write_byte(*byte); count += 1; } @@ -1878,7 +1670,7 @@ where } offset = next_offset; - UartTxFuture::new(self.uart.reborrow(), TxEvent::TxFiFoEmpty).await; + UartTxFuture::new(self.uart.reborrow(), TxEvent::FiFoEmpty).await; } Ok(count) @@ -1892,24 +1684,21 @@ where pub async fn flush_async(&mut self) -> Result<(), Error> { let count = self.tx_fifo_count(); if count > 0 { - UartTxFuture::new(self.uart.reborrow(), TxEvent::TxDone).await; + UartTxFuture::new(self.uart.reborrow(), TxEvent::Done).await; } Ok(()) } } -impl UartRx<'_, Async, T> -where - T: Instance, -{ +impl UartRx<'_, Async> { /// Read async to buffer slice `buf`. /// Waits until at least one byte is in the Rx FiFo /// and one of the following interrupts occurs: /// - `RXFIFO_FULL` /// - `RXFIFO_OVF` /// - `AT_CMD_CHAR_DET` (only if `set_at_cmd` was called) - /// - `RXFIFO_TOUT` (only if `set_rx_timeout was called) + /// - `RXFIFO_TOUT` (only if `set_rx_timeout` was called) /// /// The interrupts in question are enabled during the body of this /// function. The method immediately returns when the interrupt @@ -1922,10 +1711,10 @@ where /// /// # Ok /// When successful, returns the number of bytes written to buf. - /// This method will never return Ok(0) + /// If the passed in buffer is of length 0, Ok(0) is returned. pub async fn read_async(&mut self, buf: &mut [u8]) -> Result { if buf.is_empty() { - return Err(Error::InvalidArgument); + return Ok(0); } loop { @@ -1935,16 +1724,15 @@ where | RxEvent::GlitchDetected | RxEvent::ParityError; - let register_block = self.uart.info().register_block(); - if register_block.at_cmd_char().read().char_num().bits() > 0 { + if self.regs().at_cmd_char().read().char_num().bits() > 0 { events |= RxEvent::CmdCharDetected; } cfg_if::cfg_if! { if #[cfg(any(esp32c6, esp32h2))] { - let reg_en = register_block.tout_conf(); + let reg_en = self.regs().tout_conf(); } else { - let reg_en = register_block.conf1(); + let reg_en = self.regs().conf1(); } }; if reg_en.read().rx_tout_en().bit_is_set() { @@ -1953,25 +1741,14 @@ where let events_happened = UartRxFuture::new(self.uart.reborrow(), 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.flush_buffer(buf); // check error events - for event_happened in events_happened { - match event_happened { - RxEvent::FifoOvf => return Err(Error::RxFifoOvf), - RxEvent::GlitchDetected => return Err(Error::RxGlitchDetected), - RxEvent::FrameError => return Err(Error::RxFrameError), - RxEvent::ParityError => return Err(Error::RxParityError), - RxEvent::FifoFull - | RxEvent::CmdCharDetected - | RxEvent::BreakDetected - | RxEvent::FifoTout => continue, - } - } + rx_event_check_for_error(events_happened)?; // 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 // cleared. Since we do not drain the fifo in the interrupt handler, we need to // reset the counter here, after draining the fifo. - self.register_block() + self.regs() .int_clr() .write(|w| w.rxfifo_tout().clear_bit_by_one()); @@ -1980,21 +1757,11 @@ where } } } - - /// Interrupt-driven wait for a break condition on the RX line. - /// Condition is met when the receiver detects a NULL character (i.e. logic - /// 0 for one NULL character transmission) after stop bits. - pub async fn wait_for_break_async(&mut self) { - UartRxFuture::new(self.uart.reborrow(), RxEvent::BreakDetected).await; - } } #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io_async::Read for Uart<'_, Async, T> -where - T: Instance, -{ +impl embedded_io_async::Read for Uart<'_, Async> { /// In contrast to the documentation of embedded_io_async::Read, this /// method blocks until an uart interrupt occurs. /// See UartRx::read_async for more details. @@ -2005,10 +1772,7 @@ where #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io_async::Read for UartRx<'_, Async, T> -where - T: Instance, -{ +impl embedded_io_async::Read for UartRx<'_, Async> { /// In contrast to the documentation of embedded_io_async::Read, this /// method blocks until an uart interrupt occurs. /// See UartRx::read_async for more details. @@ -2019,10 +1783,7 @@ where #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io_async::Write for Uart<'_, Async, T> -where - T: Instance, -{ +impl embedded_io_async::Write for Uart<'_, Async> { async fn write(&mut self, buf: &[u8]) -> Result { self.write_async(buf).await } @@ -2034,10 +1795,7 @@ where #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io_async::Write for UartTx<'_, Async, T> -where - T: Instance, -{ +impl embedded_io_async::Write for UartTx<'_, Async> { async fn write(&mut self, buf: &[u8]) -> Result { self.write_async(buf).await } @@ -2052,7 +1810,7 @@ where /// bit set. The fact that an interrupt has been disabled is used by the /// futures to detect that they should indeed resolve after being woken up pub(super) fn intr_handler(uart: &Info, state: &State) { - let interrupts = uart.register_block().int_st().read(); + let interrupts = uart.regs().int_st().read(); let interrupt_bits = interrupts.bits(); // = int_raw & int_ena let rx_wake = interrupts.rxfifo_full().bit_is_set() || interrupts.rxfifo_ovf().bit_is_set() @@ -2060,13 +1818,12 @@ pub(super) fn intr_handler(uart: &Info, state: &State) { || 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() - || interrupts.brk_det().bit_is_set(); + || interrupts.parity_err().bit_is_set(); let tx_wake = interrupts.tx_done().bit_is_set() || interrupts.txfifo_empty().bit_is_set(); - uart.register_block() + uart.regs() .int_clr() .write(|w| unsafe { w.bits(interrupt_bits) }); - uart.register_block() + uart.regs() .int_ena() .modify(|r, w| unsafe { w.bits(r.bits() & !interrupt_bits) }); @@ -2080,10 +1837,11 @@ pub(super) fn intr_handler(uart: &Info, state: &State) { /// Low-power UART #[cfg(lp_uart)] +#[instability::unstable] pub mod lp_uart { use crate::{ gpio::lp_io::{LowPowerInput, LowPowerOutput}, - peripherals::{LP_CLKRST, LP_UART}, + peripherals::{LPWR, LP_AON, LP_IO, LP_UART}, uart::{Config, DataBits, Parity, StopBits}, }; /// LP-UART driver @@ -2102,47 +1860,43 @@ pub mod lp_uart { _tx: LowPowerOutput<'_, 5>, _rx: LowPowerInput<'_, 4>, ) -> Self { - let lp_io = unsafe { crate::peripherals::LP_IO::steal() }; - let lp_aon = unsafe { crate::peripherals::LP_AON::steal() }; - // FIXME: use GPIO APIs to configure pins - lp_aon + LP_AON::regs() .gpio_mux() - .modify(|r, w| unsafe { w.sel().bits(r.sel().bits() | 1 << 4) }); - lp_aon - .gpio_mux() - .modify(|r, w| unsafe { w.sel().bits(r.sel().bits() | 1 << 5) }); + .modify(|r, w| unsafe { w.sel().bits(r.sel().bits() | (1 << 4) | (1 << 5)) }); - lp_io.gpio(4).modify(|_, w| unsafe { w.mcu_sel().bits(1) }); - lp_io.gpio(5).modify(|_, w| unsafe { w.mcu_sel().bits(1) }); + LP_IO::regs() + .gpio(4) + .modify(|_, w| unsafe { w.mcu_sel().bits(1) }); + LP_IO::regs() + .gpio(5) + .modify(|_, w| unsafe { w.mcu_sel().bits(1) }); let mut me = Self { uart }; + let uart = me.uart.register_block(); // Set UART mode - do nothing for LP // Disable UART parity // 8-bit world // 1-bit stop bit - me.uart.conf0().modify(|_, w| unsafe { + uart.conf0().modify(|_, w| unsafe { w.parity().clear_bit(); w.parity_en().clear_bit(); w.bit_num().bits(0x3); w.stop_bit_num().bits(0x1) }); // Set tx idle - me.uart - .idle_conf() + uart.idle_conf() .modify(|_, w| unsafe { w.tx_idle_num().bits(0) }); // Disable hw-flow control - me.uart - .hwfc_conf() - .modify(|_, w| w.rx_flow_en().clear_bit()); + uart.hwfc_conf().modify(|_, w| w.rx_flow_en().clear_bit()); // Get source clock frequency // default == SOC_MOD_CLK_RTC_FAST == 2 - // LP_CLKRST.lpperi.lp_uart_clk_sel = 0; - unsafe { LP_CLKRST::steal() } + // LPWR.lpperi.lp_uart_clk_sel = 0; + LPWR::regs() .lpperi() .modify(|_, w| w.lp_uart_clk_sel().clear_bit()); @@ -2166,26 +1920,39 @@ pub mod lp_uart { } fn rxfifo_reset(&mut self) { - self.uart.conf0().modify(|_, w| w.rxfifo_rst().set_bit()); + self.uart + .register_block() + .conf0() + .modify(|_, w| w.rxfifo_rst().set_bit()); self.update(); - self.uart.conf0().modify(|_, w| w.rxfifo_rst().clear_bit()); + self.uart + .register_block() + .conf0() + .modify(|_, w| w.rxfifo_rst().clear_bit()); self.update(); } fn txfifo_reset(&mut self) { - self.uart.conf0().modify(|_, w| w.txfifo_rst().set_bit()); + self.uart + .register_block() + .conf0() + .modify(|_, w| w.txfifo_rst().set_bit()); self.update(); - self.uart.conf0().modify(|_, w| w.txfifo_rst().clear_bit()); + self.uart + .register_block() + .conf0() + .modify(|_, w| w.txfifo_rst().clear_bit()); self.update(); } fn update(&mut self) { - self.uart + let register_block = self.uart.register_block(); + register_block .reg_update() .modify(|_, w| w.reg_update().set_bit()); - while self.uart.reg_update().read().reg_update().bit_is_set() { + while register_block.reg_update().read().reg_update().bit_is_set() { // wait } } @@ -2196,7 +1963,7 @@ pub mod lp_uart { let max_div = 0b1111_1111_1111 - 1; let clk_div = clk.div_ceil(max_div * baudrate); - self.uart.clk_conf().modify(|_, w| unsafe { + self.uart.register_block().clk_conf().modify(|_, w| unsafe { w.sclk_div_a().bits(0); w.sclk_div_b().bits(0); w.sclk_div_num().bits(clk_div as u8 - 1); @@ -2213,6 +1980,7 @@ pub mod lp_uart { let divider = divider as u16; self.uart + .register_block() .clkdiv() .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); @@ -2227,23 +1995,28 @@ pub mod lp_uart { } fn change_parity(&mut self, parity: Parity) -> &mut Self { - if parity != Parity::ParityNone { + if parity != Parity::None { self.uart + .register_block() .conf0() .modify(|_, w| w.parity().bit((parity as u8 & 0x1) != 0)); } - self.uart.conf0().modify(|_, w| match parity { - Parity::ParityNone => w.parity_en().clear_bit(), - Parity::ParityEven => w.parity_en().set_bit().parity().clear_bit(), - Parity::ParityOdd => w.parity_en().set_bit().parity().set_bit(), - }); + self.uart + .register_block() + .conf0() + .modify(|_, w| match parity { + Parity::None => w.parity_en().clear_bit(), + Parity::Even => w.parity_en().set_bit().parity().clear_bit(), + Parity::Odd => w.parity_en().set_bit().parity().set_bit(), + }); self } fn change_data_bits(&mut self, data_bits: DataBits) -> &mut Self { self.uart + .register_block() .conf0() .modify(|_, w| unsafe { w.bit_num().bits(data_bits as u8) }); @@ -2254,6 +2027,7 @@ pub mod lp_uart { fn change_stop_bits(&mut self, stop_bits: StopBits) -> &mut Self { self.uart + .register_block() .conf0() .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) }); @@ -2263,6 +2037,7 @@ pub mod lp_uart { fn change_tx_idle(&mut self, idle_num: u16) -> &mut Self { self.uart + .register_block() .idle_conf() .modify(|_, w| unsafe { w.tx_idle_num().bits(idle_num) }); @@ -2273,6 +2048,7 @@ pub mod lp_uart { } /// UART Peripheral Instance +#[doc(hidden)] pub trait Instance: Peripheral

+ Into + 'static { /// Returns the peripheral data and state describing this UART instance. fn parts(&self) -> (&'static Info, &'static State); @@ -2291,6 +2067,7 @@ pub trait Instance: Peripheral

+ Into + 'static { } /// Peripheral data describing a particular UART instance. +#[doc(hidden)] #[non_exhaustive] pub struct Info { /// Pointer to the register block for this UART instance. @@ -2321,6 +2098,7 @@ pub struct Info { } /// Peripheral state for a UART instance. +#[doc(hidden)] #[non_exhaustive] pub struct State { /// Waker for the asynchronous RX operations. @@ -2338,13 +2116,13 @@ pub struct State { impl Info { /// Returns the register block for this UART instance. - pub fn register_block(&self) -> &RegisterBlock { + pub fn regs(&self) -> &RegisterBlock { unsafe { &*self.register_block } } /// Listen for the given interrupts fn enable_listen(&self, interrupts: EnumSet, enable: bool) { - let reg_block = self.register_block(); + let reg_block = self.regs(); reg_block.int_ena().modify(|_, w| { for interrupt in interrupts { @@ -2352,7 +2130,6 @@ impl Info { UartInterrupt::AtCmd => w.at_cmd_char_det().bit(enable), UartInterrupt::TxDone => w.tx_done().bit(enable), UartInterrupt::RxFifoFull => w.rxfifo_full().bit(enable), - UartInterrupt::RxBreakDetected => w.brk_det().bit(enable), }; } w @@ -2361,7 +2138,7 @@ impl Info { fn interrupts(&self) -> EnumSet { let mut res = EnumSet::new(); - let reg_block = self.register_block(); + let reg_block = self.regs(); let ints = reg_block.int_raw().read(); @@ -2374,15 +2151,12 @@ impl Info { if ints.rxfifo_full().bit_is_set() { res.insert(UartInterrupt::RxFifoFull); } - if ints.brk_det().bit_is_set() { - res.insert(UartInterrupt::RxBreakDetected); - } res } fn clear_interrupts(&self, interrupts: EnumSet) { - let reg_block = self.register_block(); + let reg_block = self.regs(); reg_block.int_clr().write(|w| { for interrupt in interrupts { @@ -2390,7 +2164,6 @@ impl Info { UartInterrupt::AtCmd => w.at_cmd_char_det().clear_bit_by_one(), UartInterrupt::TxDone => w.tx_done().clear_bit_by_one(), UartInterrupt::RxFifoFull => w.rxfifo_full().clear_bit_by_one(), - UartInterrupt::RxBreakDetected => w.brk_det().clear_bit_by_one(), }; } w @@ -2412,8 +2185,6 @@ impl Info { } fn apply_config(&self, config: &Config) -> Result<(), ConfigError> { - self.set_rx_fifo_full_threshold(config.rx_fifo_full_threshold)?; - self.set_rx_timeout(config.rx_timeout, config.symbol_length())?; self.change_baud(config.baudrate, config.clock_source); self.change_data_bits(config.data_bits); self.change_parity(config.parity); @@ -2426,6 +2197,87 @@ impl Info { Ok(()) } + fn enable_listen_rx(&self, events: EnumSet, enable: bool) { + self.regs().int_ena().modify(|_, w| { + for event in events { + match event { + RxEvent::FifoFull => w.rxfifo_full().bit(enable), + RxEvent::CmdCharDetected => w.at_cmd_char_det().bit(enable), + + RxEvent::FifoOvf => w.rxfifo_ovf().bit(enable), + RxEvent::FifoTout => w.rxfifo_tout().bit(enable), + RxEvent::GlitchDetected => w.glitch_det().bit(enable), + RxEvent::FrameError => w.frm_err().bit(enable), + RxEvent::ParityError => w.parity_err().bit(enable), + }; + } + w + }); + } + + fn enabled_rx_events(&self, events: impl Into>) -> EnumSet { + let events = events.into(); + let interrupts_enabled = self.regs().int_ena().read(); + let mut events_triggered = EnumSet::new(); + for event in events { + let event_triggered = match event { + RxEvent::FifoFull => interrupts_enabled.rxfifo_full().bit_is_clear(), + RxEvent::CmdCharDetected => interrupts_enabled.at_cmd_char_det().bit_is_clear(), + + RxEvent::FifoOvf => interrupts_enabled.rxfifo_ovf().bit_is_clear(), + RxEvent::FifoTout => interrupts_enabled.rxfifo_tout().bit_is_clear(), + RxEvent::GlitchDetected => interrupts_enabled.glitch_det().bit_is_clear(), + RxEvent::FrameError => interrupts_enabled.frm_err().bit_is_clear(), + RxEvent::ParityError => interrupts_enabled.parity_err().bit_is_clear(), + }; + if event_triggered { + events_triggered |= event; + } + } + events_triggered + } + + fn rx_events(&self, events: impl Into>) -> EnumSet { + let events = events.into(); + let interrupts_enabled = self.regs().int_st().read(); + let mut events_triggered = EnumSet::new(); + for event in events { + let event_triggered = match event { + RxEvent::FifoFull => interrupts_enabled.rxfifo_full().bit_is_set(), + RxEvent::CmdCharDetected => interrupts_enabled.at_cmd_char_det().bit_is_set(), + + RxEvent::FifoOvf => interrupts_enabled.rxfifo_ovf().bit_is_set(), + RxEvent::FifoTout => interrupts_enabled.rxfifo_tout().bit_is_set(), + RxEvent::GlitchDetected => interrupts_enabled.glitch_det().bit_is_set(), + RxEvent::FrameError => interrupts_enabled.frm_err().bit_is_set(), + RxEvent::ParityError => interrupts_enabled.parity_err().bit_is_set(), + }; + if event_triggered { + events_triggered |= event; + } + } + events_triggered + } + + fn clear_rx_events(&self, events: impl Into>) { + let events = events.into(); + self.regs().int_clr().write(|w| { + for event in events { + match event { + RxEvent::FifoFull => w.rxfifo_full().clear_bit_by_one(), + RxEvent::CmdCharDetected => w.at_cmd_char_det().clear_bit_by_one(), + + RxEvent::FifoOvf => w.rxfifo_ovf().clear_bit_by_one(), + RxEvent::FifoTout => w.rxfifo_tout().clear_bit_by_one(), + RxEvent::GlitchDetected => w.glitch_det().clear_bit_by_one(), + RxEvent::FrameError => w.frm_err().clear_bit_by_one(), + RxEvent::ParityError => w.parity_err().clear_bit_by_one(), + }; + } + w + }); + } + /// Configures the RX-FIFO threshold /// /// # Errors @@ -2452,7 +2304,7 @@ impl Info { return Err(ConfigError::UnsupportedFifoThreshold); } - self.register_block() + self.regs() .conf1() .modify(|_, w| unsafe { w.rxfifo_full_thrhd().bits(threshold as _) }); @@ -2481,7 +2333,7 @@ impl Info { } } - let register_block = self.register_block(); + let register_block = self.regs(); if let Some(timeout) = timeout { // the esp32 counts directly in number of symbols (symbol len fixed to 8) @@ -2523,6 +2375,8 @@ impl Info { #[cfg(any(esp32c2, esp32c3, esp32s3))] fn change_baud(&self, baudrate: u32, clock_source: ClockSource) { + use crate::peripherals::LPWR; + let clocks = Clocks::get(); let clk = match clock_source { ClockSource::Apb => clocks.apb_clock.to_Hz(), @@ -2531,7 +2385,7 @@ impl Info { }; if clock_source == ClockSource::RcFast { - unsafe { crate::peripherals::RTC_CNTL::steal() } + LPWR::regs() .clk_conf() .modify(|_, w| w.dig_clk8m_en().variant(true)); // esp_rom_delay_us(SOC_DELAY_RC_FAST_DIGI_SWITCH); @@ -2540,7 +2394,7 @@ impl Info { let max_div = 0b1111_1111_1111 - 1; let clk_div = clk.div_ceil(max_div * baudrate); - self.register_block().clk_conf().write(|w| unsafe { + self.regs().clk_conf().write(|w| unsafe { w.sclk_sel().bits(match clock_source { ClockSource::Apb => 1, ClockSource::RcFast => 2, @@ -2556,7 +2410,7 @@ impl Info { let divider = (clk << 4) / (baudrate * clk_div); let divider_integer = (divider >> 4) as u16; let divider_frag = (divider & 0xf) as u8; - self.register_block() + self.regs() .clkdiv() .write(|w| unsafe { w.clkdiv().bits(divider_integer).frag().bits(divider_frag) }); } @@ -2566,7 +2420,7 @@ impl Info { } fn sync_regs(&self) { - sync_regs(self.register_block()); + sync_regs(self.regs()); } #[cfg(any(esp32c6, esp32h2))] @@ -2582,7 +2436,7 @@ impl Info { let clk_div = clk.div_ceil(max_div * baudrate); // UART clocks are configured via PCR - let pcr = unsafe { crate::peripherals::PCR::steal() }; + let pcr = crate::peripherals::PCR::regs(); if self.is_instance(unsafe { crate::peripherals::UART0::steal() }) { pcr.uart0_conf() @@ -2620,7 +2474,7 @@ impl Info { let divider = clk / baudrate; let divider = divider as u16; - self.register_block() + self.regs() .clkdiv() .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); @@ -2635,28 +2489,28 @@ impl Info { ClockSource::RefTick => crate::soc::constants::REF_TICK.to_Hz(), }; - self.register_block() + self.regs() .conf0() .modify(|_, w| w.tick_ref_always_on().bit(clock_source == ClockSource::Apb)); let divider = clk / baudrate; - self.register_block() + self.regs() .clkdiv() .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); } fn change_data_bits(&self, data_bits: DataBits) { - self.register_block() + self.regs() .conf0() .modify(|_, w| unsafe { w.bit_num().bits(data_bits as u8) }); } fn change_parity(&self, parity: Parity) { - self.register_block().conf0().modify(|_, w| match parity { - Parity::ParityNone => w.parity_en().clear_bit(), - Parity::ParityEven => w.parity_en().set_bit().parity().clear_bit(), - Parity::ParityOdd => w.parity_en().set_bit().parity().set_bit(), + self.regs().conf0().modify(|_, w| match parity { + Parity::None => w.parity_en().clear_bit(), + Parity::Even => w.parity_en().set_bit().parity().clear_bit(), + Parity::Odd => w.parity_en().set_bit().parity().set_bit(), }); } @@ -2664,23 +2518,19 @@ impl Info { #[cfg(esp32)] { // workaround for hardware issue, when UART stop bit set as 2-bit mode. - if stop_bits == StopBits::Stop2 { - self.register_block() + if stop_bits == StopBits::_2 { + self.regs() .rs485_conf() - .modify(|_, w| w.dl1_en().bit(stop_bits == StopBits::Stop2)); - - self.register_block().conf0().modify(|_, w| { - if stop_bits == StopBits::Stop2 { - unsafe { w.stop_bit_num().bits(1) } - } else { - unsafe { w.stop_bit_num().bits(stop_bits as u8) } - } - }); + .modify(|_, w| w.dl1_en().bit(stop_bits == StopBits::_2)); + + self.regs() + .conf0() + .modify(|_, w| unsafe { w.stop_bit_num().bits(1) }); } } #[cfg(not(esp32))] - self.register_block() + self.regs() .conf0() .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) }); } @@ -2691,8 +2541,8 @@ impl Info { sync_regs(reg_block); } - rxfifo_rst(self.register_block(), true); - rxfifo_rst(self.register_block(), false); + rxfifo_rst(self.regs(), true); + rxfifo_rst(self.regs(), false); } fn txfifo_reset(&self) { @@ -2701,8 +2551,8 @@ impl Info { sync_regs(reg_block); } - txfifo_rst(self.register_block(), true); - txfifo_rst(self.register_block(), false); + txfifo_rst(self.regs(), true); + txfifo_rst(self.regs(), false); } } @@ -2718,7 +2568,7 @@ macro_rules! impl_instance { ($inst:ident, $peri:ident, $txd:ident, $rxd:ident, $cts:ident, $rts:ident) => { impl Instance for crate::peripherals::$inst { fn parts(&self) -> (&'static Info, &'static State) { - #[crate::macros::handler] + #[crate::handler] pub(super) fn irq_handler() { intr_handler(&PERIPHERAL, &STATE); } @@ -2775,4 +2625,4 @@ impl Instance for AnyUart { AnyUartInner::Uart2(uart) => uart.parts(), } } -} +} \ No newline at end of file From cdb7bdc4ab718461e0a54b4cc7d920c8c4eca14c Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sat, 25 Jan 2025 20:57:46 -0500 Subject: [PATCH 23/34] feat: migrate to v0.23 --- esp-hal/src/uart.rs | 59 ++++++++++++++++++- examples/src/bin/uart_break_detection.rs | 31 ++++------ .../src/bin/uart_break_detection_async.rs | 33 ++++------- examples/src/bin/uart_interrupts.rs | 34 +++++------ hil-test/tests/uart_brk_det.rs | 8 ++- 5 files changed, 104 insertions(+), 61 deletions(-) diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index 40bd3d4a266..eb82d0f44bb 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -852,6 +852,23 @@ where Ok(count) } + /// Busy waits for a break condition to be detected on the RX + /// line. Condition is met when the receiver detects a NULL character + /// (i.e. logic 0 for one NULL character transmission) after stop bits. + /// + /// Clears the break detection interrupt before returning. + pub fn wait_for_break(&mut self) { + // Enable the break detection interrupt + self.regs().int_ena().write(|w| w.brk_det().bit(true)); + + while !self.regs().int_raw().read().brk_det().bit() { + // Just busy waiting + } + + // Clear the break detection interrupt + self.regs().int_clr().write(|w| w.brk_det().bit(true)); + } + /// Read bytes from the RX FIFO without checking for errors. fn flush_buffer(&mut self, buf: &mut [u8]) -> usize { let mut count = 0; @@ -1047,6 +1064,11 @@ pub enum UartInterrupt { /// The transmitter has finished sending out all data from the FIFO. TxDone, + /// Break condition has been detected. + /// Triggered when the receiver detects a NULL character (i.e. logic 0 for + /// one NULL character transmission) after stop bits. + RxBreakDetected, + /// The receiver has received more data than what /// [`RxConfig::fifo_full_threshold`] specifies. RxFifoFull, @@ -1170,6 +1192,11 @@ where sync_regs(self.regs()); } + /// Busy waits for a break condition to be detected on the RX line. + pub fn wait_for_break(&mut self) { + self.rx.wait_for_break(); + } + /// Flush the transmit buffer of the UART pub fn flush(&mut self) { self.tx.flush() @@ -1475,6 +1502,7 @@ pub(crate) enum TxEvent { #[derive(Debug, EnumSetType)] pub(crate) enum RxEvent { FifoFull, + BreakDetected, CmdCharDetected, FifoOvf, FifoTout, @@ -1490,7 +1518,10 @@ fn rx_event_check_for_error(events: EnumSet) -> Result<(), Error> { RxEvent::GlitchDetected => return Err(Error::GlitchOccurred), RxEvent::FrameError => return Err(Error::FrameFormatViolated), RxEvent::ParityError => return Err(Error::ParityMismatch), - RxEvent::FifoFull | RxEvent::CmdCharDetected | RxEvent::FifoTout => continue, + RxEvent::FifoFull + | RxEvent::BreakDetected + | RxEvent::CmdCharDetected + | RxEvent::FifoTout => continue, } } @@ -1633,6 +1664,13 @@ impl Uart<'_, Async> { self.rx.read_async(buf).await } + /// Asynchronously waits for a break condition on the RX line. + /// Condition is met when the receiver detects a NULL character (i.e. logic + /// 0 for one NULL character transmission) after stop bits. + pub async fn wait_for_break_async(&mut self) { + self.rx.wait_for_break_async().await; + } + /// Asynchronously writes data to the UART transmit buffer. pub async fn write_async(&mut self, words: &[u8]) -> Result { self.tx.write_async(words).await @@ -1757,6 +1795,13 @@ impl UartRx<'_, Async> { } } } + + /// Interrupt-driven wait for a break condition on the RX line. + /// Condition is met when the receiver detects a NULL character (i.e. logic + /// 0 for one NULL character transmission) after stop bits. + pub async fn wait_for_break_async(&mut self) { + UartRxFuture::new(self.uart.reborrow(), RxEvent::BreakDetected).await; + } } #[cfg(any(doc, feature = "unstable"))] @@ -1815,6 +1860,7 @@ pub(super) fn intr_handler(uart: &Info, state: &State) { let rx_wake = interrupts.rxfifo_full().bit_is_set() || interrupts.rxfifo_ovf().bit_is_set() || interrupts.rxfifo_tout().bit_is_set() + || interrupts.brk_det().bit_is_set() || interrupts.at_cmd_char_det().bit_is_set() || interrupts.glitch_det().bit_is_set() || interrupts.frm_err().bit_is_set() @@ -2130,6 +2176,7 @@ impl Info { UartInterrupt::AtCmd => w.at_cmd_char_det().bit(enable), UartInterrupt::TxDone => w.tx_done().bit(enable), UartInterrupt::RxFifoFull => w.rxfifo_full().bit(enable), + UartInterrupt::RxBreakDetected => w.brk_det().bit(enable), }; } w @@ -2151,6 +2198,9 @@ impl Info { if ints.rxfifo_full().bit_is_set() { res.insert(UartInterrupt::RxFifoFull); } + if ints.brk_det().bit_is_set() { + res.insert(UartInterrupt::RxBreakDetected); + } res } @@ -2164,6 +2214,7 @@ impl Info { UartInterrupt::AtCmd => w.at_cmd_char_det().clear_bit_by_one(), UartInterrupt::TxDone => w.tx_done().clear_bit_by_one(), UartInterrupt::RxFifoFull => w.rxfifo_full().clear_bit_by_one(), + UartInterrupt::RxBreakDetected => w.brk_det().clear_bit_by_one(), }; } w @@ -2202,6 +2253,7 @@ impl Info { for event in events { match event { RxEvent::FifoFull => w.rxfifo_full().bit(enable), + RxEvent::BreakDetected => w.brk_det().bit(enable), RxEvent::CmdCharDetected => w.at_cmd_char_det().bit(enable), RxEvent::FifoOvf => w.rxfifo_ovf().bit(enable), @@ -2222,6 +2274,7 @@ impl Info { for event in events { let event_triggered = match event { RxEvent::FifoFull => interrupts_enabled.rxfifo_full().bit_is_clear(), + RxEvent::BreakDetected => interrupts_enabled.brk_det().bit_is_clear(), RxEvent::CmdCharDetected => interrupts_enabled.at_cmd_char_det().bit_is_clear(), RxEvent::FifoOvf => interrupts_enabled.rxfifo_ovf().bit_is_clear(), @@ -2244,6 +2297,7 @@ impl Info { for event in events { let event_triggered = match event { RxEvent::FifoFull => interrupts_enabled.rxfifo_full().bit_is_set(), + RxEvent::BreakDetected => interrupts_enabled.brk_det().bit_is_set(), RxEvent::CmdCharDetected => interrupts_enabled.at_cmd_char_det().bit_is_set(), RxEvent::FifoOvf => interrupts_enabled.rxfifo_ovf().bit_is_set(), @@ -2265,6 +2319,7 @@ impl Info { for event in events { match event { RxEvent::FifoFull => w.rxfifo_full().clear_bit_by_one(), + RxEvent::BreakDetected => w.brk_det().clear_bit_by_one(), RxEvent::CmdCharDetected => w.at_cmd_char_det().clear_bit_by_one(), RxEvent::FifoOvf => w.rxfifo_ovf().clear_bit_by_one(), @@ -2625,4 +2680,4 @@ impl Instance for AnyUart { AnyUartInner::Uart2(uart) => uart.parts(), } } -} \ No newline at end of file +} diff --git a/examples/src/bin/uart_break_detection.rs b/examples/src/bin/uart_break_detection.rs index d92c8d90ec7..1203c3a95fd 100644 --- a/examples/src/bin/uart_break_detection.rs +++ b/examples/src/bin/uart_break_detection.rs @@ -11,33 +11,26 @@ use esp_backtrace as _; use esp_hal::{ - entry, - uart::{Config as UartConfig, DataBits, StopBits, Uart}, + main, + uart::{Config as UartConfig, DataBits, Parity, RxConfig, StopBits, Uart}, }; -#[entry] +#[main] fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); let uart_config = UartConfig::default() - .baudrate(19200) - .data_bits(DataBits::DataBits8) - .parity_none() - .stop_bits(StopBits::Stop1) - .rx_fifo_full_threshold(1); // interrupt every time a byte is received - let mut uart = Uart::new( - peripherals.UART1, - uart_config, - peripherals.GPIO16, // RX - peripherals.GPIO17, // TX - ) - .expect("Failed to initialize UART"); + .with_baudrate(19200) + .with_data_bits(DataBits::_8) + .with_parity(Parity::None) + .with_stop_bits(StopBits::_1) + .with_rx(RxConfig::default().with_fifo_full_threshold(1)); + let mut uart = Uart::new(peripherals.UART1, uart_config) + .expect("Failed to initialize UART") + .with_rx(peripherals.GPIO16) + .with_tx(peripherals.GPIO17); loop { uart.wait_for_break(); esp_println::print!("\nBREAK"); - - while let Ok(byte) = uart.read_byte() { - esp_println::print!(" {:02X}", byte); - } } } diff --git a/examples/src/bin/uart_break_detection_async.rs b/examples/src/bin/uart_break_detection_async.rs index b574d530f04..7b1fcfee2ce 100644 --- a/examples/src/bin/uart_break_detection_async.rs +++ b/examples/src/bin/uart_break_detection_async.rs @@ -5,41 +5,32 @@ //! - RX => GPIO16 //% CHIPS: esp32 -//% FEATURES: embassy embassy-generic-timers esp-hal/unstable +//% FEATURES: embassy esp-hal/unstable #![no_std] #![no_main] use embassy_executor::Spawner; use esp_backtrace as _; -use esp_hal::uart::{Config as UartConfig, DataBits, StopBits, Uart}; +use esp_hal::uart::{Config as UartConfig, DataBits, Parity, RxConfig, StopBits, Uart}; #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { let peripherals = esp_hal::init(esp_hal::Config::default()); let uart_config = UartConfig::default() - .baudrate(19200) - .data_bits(DataBits::DataBits8) - .parity_none() - .stop_bits(StopBits::Stop1) - .rx_fifo_full_threshold(1); // interrupt every time a byte is received - let mut uart = Uart::new( - peripherals.UART1, - uart_config, - peripherals.GPIO16, // RX - peripherals.GPIO17, // TX - ) - .expect("Failed to initialize UART") - .into_async(); + .with_baudrate(19200) + .with_data_bits(DataBits::_8) + .with_parity(Parity::None) + .with_stop_bits(StopBits::_1) + .with_rx(RxConfig::default().with_fifo_full_threshold(1)); + let mut uart = Uart::new(peripherals.UART1, uart_config) + .expect("Failed to initialize UART") + .with_rx(peripherals.GPIO16) + .with_tx(peripherals.GPIO17) + .into_async(); loop { uart.wait_for_break_async().await; esp_println::print!("\nBREAK"); - - let mut buf = [0u8; 11]; - let len = uart.read_async(&mut buf).await.unwrap(); - for byte in buf.iter().take(len) { - esp_println::print!(" {:02X}", byte); - } } } diff --git a/examples/src/bin/uart_interrupts.rs b/examples/src/bin/uart_interrupts.rs index c4e8d7d2e19..d8acffee442 100644 --- a/examples/src/bin/uart_interrupts.rs +++ b/examples/src/bin/uart_interrupts.rs @@ -14,31 +14,29 @@ use core::cell::RefCell; use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ - entry, + handler, interrupt::InterruptConfigurable, - macros::{handler, ram}, - uart::{Config as UartConfig, DataBits, StopBits, Uart, UartInterrupt}, + main, + ram, + uart::{Config as UartConfig, DataBits, Parity, RxConfig, StopBits, Uart, UartInterrupt}, Blocking, }; static SERIAL: Mutex>>> = Mutex::new(RefCell::new(None)); -#[entry] +#[main] fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); let uart_config = UartConfig::default() - .baudrate(19200) - .data_bits(DataBits::DataBits8) - .parity_none() - .stop_bits(StopBits::Stop1) - .rx_fifo_full_threshold(1); // interrupt every time a byte is received - let mut uart = Uart::new( - peripherals.UART1, - uart_config, - peripherals.GPIO16, // RX - peripherals.GPIO17, // TX - ) - .expect("Failed to initialize UART"); + .with_baudrate(19200) + .with_data_bits(DataBits::_8) + .with_parity(Parity::None) + .with_stop_bits(StopBits::_1) + .with_rx(RxConfig::default().with_fifo_full_threshold(1)); + let mut uart = Uart::new(peripherals.UART1, uart_config) + .expect("Failed to initialize UART") + .with_rx(peripherals.GPIO16) + .with_tx(peripherals.GPIO17); uart.set_interrupt_handler(handler); @@ -62,7 +60,9 @@ fn handler() { esp_println::print!("\nBREAK"); } if serial.interrupts().contains(UartInterrupt::RxFifoFull) { - esp_println::print!(" {:02X}", serial.read_byte().expect("Read byte failed")); + let mut byte = [0u8; 1]; + serial.read_bytes(&mut byte).unwrap(); + esp_println::print!(" {:02X}", byte[0]); } serial.clear_interrupts(UartInterrupt::RxBreakDetected | UartInterrupt::RxFifoFull); diff --git a/hil-test/tests/uart_brk_det.rs b/hil-test/tests/uart_brk_det.rs index 3d4c49177ee..dd85f95aa57 100644 --- a/hil-test/tests/uart_brk_det.rs +++ b/hil-test/tests/uart_brk_det.rs @@ -1,12 +1,13 @@ //! UART Break Detection test //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: embassy #![no_std] #![no_main] use esp_hal::{ - uart::{self, Uart}, + uart::{Uart, Config as UartConfig,}, Blocking, }; use hil_test as _; @@ -26,7 +27,10 @@ mod tests { let (_, pin) = hil_test::common_test_pins!(peripherals); let (rx, tx) = pin.split(); - let uart = Uart::new(peripherals.UART1, uart::Config::default(), rx, tx).unwrap(); + let uart = Uart::new(peripherals.UART1, UartConfig::default()) + .expect("Failed to initialize UART") + .with_rx(rx) + .with_tx(tx); Context { uart } } From 049ee9562a2e1e00a9af101b52b09549074a36db Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sat, 25 Jan 2025 21:01:09 -0500 Subject: [PATCH 24/34] chore: add back into changelog --- esp-hal/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 10de537ef2a..a0f57f3ce41 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - SPI: Added support for 3-wire SPI (#2919) - Add separate config for Rx and Tx (UART) #2965 +- Additional interrupt available in `esp_hal::uart::UartInterrupt` - as well as UartRx functions `wait_for_break()` and `wait_for_break_async().await` (#2858) ### Changed From 9f908e9742591d9e91641aa7b5de943e7f182de8 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sat, 25 Jan 2025 21:02:16 -0500 Subject: [PATCH 25/34] chore: fmt hil test --- hil-test/tests/uart_brk_det.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hil-test/tests/uart_brk_det.rs b/hil-test/tests/uart_brk_det.rs index dd85f95aa57..92dfe167c8f 100644 --- a/hil-test/tests/uart_brk_det.rs +++ b/hil-test/tests/uart_brk_det.rs @@ -7,7 +7,7 @@ #![no_main] use esp_hal::{ - uart::{Uart, Config as UartConfig,}, + uart::{Config as UartConfig, Uart}, Blocking, }; use hil_test as _; From 772a297ddbfa0f4672e857b4e1818bc4a867289b Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sat, 25 Jan 2025 21:21:16 -0500 Subject: [PATCH 26/34] fix: unused import --- examples/src/bin/uart_interrupts.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/src/bin/uart_interrupts.rs b/examples/src/bin/uart_interrupts.rs index d8acffee442..c0da47ab4d5 100644 --- a/examples/src/bin/uart_interrupts.rs +++ b/examples/src/bin/uart_interrupts.rs @@ -15,7 +15,6 @@ use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ handler, - interrupt::InterruptConfigurable, main, ram, uart::{Config as UartConfig, DataBits, Parity, RxConfig, StopBits, Uart, UartInterrupt}, From ef343f3865e5d8fa0148e9e48d5e5a9e5b539f8f Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Tue, 25 Feb 2025 15:08:11 -0500 Subject: [PATCH 27/34] fix: managing merge conflicts --- esp-hal/CHANGELOG.md | 63 +- esp-hal/src/uart.rs | 1795 ++++++++++++++++++++++++++---------------- 2 files changed, 1149 insertions(+), 709 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index a0f57f3ce41..dc39ea46a50 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -8,43 +8,81 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added + +### Changed + +### Fixed + +### Removed + +## v1.0.0-beta.0 + +### Added + - SPI: Added support for 3-wire SPI (#2919) -- Add separate config for Rx and Tx (UART) #2965 -- Additional interrupt available in `esp_hal::uart::UartInterrupt` - as well as UartRx functions `wait_for_break()` and `wait_for_break_async().await` (#2858) +- UART: Add separate config for Rx and Tx (#2965) +- UART: `read_exact_async` (unstable) (#3142) +- UART: `TxConfig::fifo_empty_threshold` (#3142) +- Added accessor methods to config structs (#3011) +- `esp_hal::time::{Rate, Duration, Instant}` (#3083) +- Async support for ADC oneshot reads for ESP32C2, ESP32C3, ESP32C6 and ESP32H2 (#2925, #3082) +- `ESP_HAL_CONFIG_XTAL_FREQUENCY` configuration. For now, chips other than ESP32 and ESP32-C2 have a single option only. (#3054) +- Added more validation to UART and SPI. User can now specify the baudrate tolerance of UART config (#3074) +- Add auto-writeback support to DMA buffers (#3107) ### Changed +- LEDC: Derive `Clone` and `Copy` for ledc speed types to make `ledc::channel::Config` derive them too. (#3139) +- The `unstable` feature is no longer enabled by default (#3136) - RMT: `TxChannelConfig` and `RxChannelConfig` now support the builder-lite pattern (#2978) - RMT: Some fields of `TxChannelConfig` and `RxChannelConfig` are now `gpio::Level`-valued instead of `bool` (#2989) - RMT: The `PulseCode` trait now uses `gpio::Level` to specify output levels instead of `bool` (#2989) -- Uart `write_bytes` and `read_bytes` are now blocking and return the number of bytes written/read (#2882) -- Uart `read_bytes` is now blocking returns the number of bytes read (#2882) -- Uart `flush` is now blocking (#2882) - Removed `embedded-hal-nb` traits (#2882) - `timer::wait` is now blocking (#2882) - By default, set `tx_idle_num` to 0 so that bytes written to TX FIFO are always immediately transmitted. (#2859) - `Rng` and `Trng` now implement `Peripheral

` (#2992) - SPI, UART, I2C: `with_` functions of peripheral drivers now disconnect the previously assigned pins from the peripheral. (#3012) - SPI, UART, I2C: Dropping a driver now disconnects pins from their peripherals. (#3012) - +- TWAI: Async transmission future resolves after successful transmission and can be aborted by dropping the future. +- Migrate PARL_IO driver to DMA move API (#3033) - `Async` drivers are no longer `Send` (#2980) -- GPIO drivers now take configuration structs, and their constructors are fallible (#2990) -- `flip-link` feature is now a config option -- `flip-link` feature is now a config option (`ESP_HAL_CONFIG_FLIP_LINK`) - +- GPIO drivers now take configuration structs (#2990, #3029) - `flip-link` feature is now a config option (`ESP_HAL_CONFIG_FLIP_LINK`) (#3001) - +- Migrate AES driver to DMA move API (#3084) - Removed features `psram-quad` and `psram-octal` - replaced by `psram` and the `ESP_HAL_CONFIG_PSRAM_MODE` (`quad`/`octal`) (#3001) +- The `esp_hal::time` module no longer reexports `fugit` types (#3083) +- The `system::RadioClockController` trait has been replaced by the `clock::RadioClockController` struct. (#3100) +- The `Cpu` struct and contents of the `reset` and `cpu_control` modules have been moved into `cpu`. (#3099) +- The `software_reset_cpu` now takes which CPU to reset as parameter. (#3099) +- `read_bytes` and `write_bytes` methods on drivers have been renamed to `read` and `write` (#3137) +- `Uart::write` and `Uart::read` are now blocking and return the number of bytes written/read (#2882) +- `Uart::flush` is now blocking (#2882) +- `Uart::split` and the respective split halves have been marked as unstable (#3137) +- Uart errors have been split into `RxError` and `TxError`. A combined `IoError` has been created for embedded-io. (#3138) +- `{Uart, UartTx}::flush()` is now fallible. (#3138) +- `Uart::{read_async, write_async}` are now cancellation-safe (#3142) +- I2C: Async functions are postfixed with `_async`, non-async functions are available in async-mode (#3056) +- ESP32-H2/ESP32-C6: Don't rely on the bootloader to deconfigure permission control (#3150) ### Fixed - `DmaDescriptor` is now `#[repr(C)]` (#2988) - Fixed an issue that caused LCD_CAM drivers to turn off their clocks unexpectedly (#3007) - Fixed an issue where DMA-driver peripherals started transferring before the data was ready (#3003) +- Fixed an issue on ESP32 and S2 where short asynchronous Timer delays would never resolve (#3093) +- Fixed an issue setting higher UART baud rates (#3104) +- ESP32-S2: Fixed linker script (#3096) +- Fix auto writeback on Crypto DMA (#3108) +- `Uart::flush()` now correctly blocks until the TX FIFO is empty (#3151) ### Removed - Removed `Pin`, `RtcPin` and `RtcPinWithResistors` implementations from `Flex` (#2938) +- OutputOpenDrain has been removed (#3029) +- The fields of config structs are no longer public (#3011) +- Removed the dysfunctional `DmaChannel::set_priority` function (#3088) +- `esp_hal::time::now()`, which has been replaced by `esp_hal::time::Instant::now()` (#3083) +- `peripherals::Interrupts` (#3152) ## [0.23.1] - 2025-01-15 @@ -1106,7 +1144,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.1.0] - 2022-08-05 -[Unreleased]: https://github.com/esp-rs/esp-hal/compare/v0.23.1...HEAD +[Unreleased]: https://github.com/esp-rs/esp-hal/compare/esp-hal-v1.0.0-beta.0...HEAD +[v1.0.0-beta.0]: https://github.com/esp-rs/esp-hal/compare/v0.23.1..esp-hal-v1.0.0-beta.0 [0.23.1]: https://github.com/esp-rs/esp-hal/compare/v0.23.0..v0.23.1 [0.23.0]: https://github.com/esp-rs/esp-hal/compare/v0.22.0..v0.23.0 [0.22.0]: https://github.com/esp-rs/esp-hal/compare/v0.21.1...v0.22.0 diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index eb82d0f44bb..411f251c27b 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -36,79 +36,6 @@ //! available. See the examples below for more information on how to interact //! with this driver. //! -//! ## Example -//! -//! ### Handling UART Interrupts -//! Notice, that in practice a proper serial terminal should be used -//! to connect to the board (espmonitor and espflash won't work) -//! ```rust, no_run -#![doc = crate::before_snippet!()] -//! # use esp_hal::delay::Delay; -//! # use esp_hal::uart::{AtCmdConfig, Config, RxConfig, Uart, UartInterrupt}; -//! # let delay = Delay::new(); -//! # let config = Config::default().with_rx( -//! RxConfig::default().with_fifo_full_threshold(30) -//! ); -//! # let mut uart0 = Uart::new( -//! # peripherals.UART0, -//! # config) -//! # .unwrap(); -//! uart0.set_interrupt_handler(interrupt_handler); -//! -//! critical_section::with(|cs| { -//! uart0.set_at_cmd(AtCmdConfig::default().with_cmd_char(b'#')); -//! uart0.listen(UartInterrupt::AtCmd | UartInterrupt::RxFifoFull); -//! -//! SERIAL.borrow_ref_mut(cs).replace(uart0); -//! }); -//! -//! loop { -//! critical_section::with(|cs| { -//! let mut serial = SERIAL.borrow_ref_mut(cs); -//! let serial = serial.as_mut().unwrap(); -//! writeln!(serial, -//! "Hello World! Send a single `#` character or send -//! at least 30 characters to trigger interrupts.") -//! .ok(); -//! }); -//! delay.delay(1.secs()); -//! } -//! # } -//! -//! # use core::cell::RefCell; -//! # use critical_section::Mutex; -//! # use esp_hal::uart::Uart; -//! static SERIAL: Mutex>>> = -//! Mutex::new(RefCell::new(None)); -//! -//! # use esp_hal::uart::UartInterrupt; -//! # use core::fmt::Write; -//! #[handler] -//! fn interrupt_handler() { -//! critical_section::with(|cs| { -//! let mut serial = SERIAL.borrow_ref_mut(cs); -//! let serial = serial.as_mut().unwrap(); -//! -//! let mut buf = [0u8; 64]; -//! if let Ok(cnt) = serial.read_buffered_bytes(&mut buf) { -//! writeln!(serial, "Read {} bytes", cnt).ok(); -//! } -//! -//! let pending_interrupts = serial.interrupts(); -//! writeln!( -//! serial, -//! "Interrupt AT-CMD: {} RX-FIFO-FULL: {}", -//! pending_interrupts.contains(UartInterrupt::AtCmd), -//! pending_interrupts.contains(UartInterrupt::RxFifoFull), -//! ).ok(); -//! -//! serial.clear_interrupts( -//! UartInterrupt::AtCmd | UartInterrupt::RxFifoFull -//! ); -//! }); -//! } -//! ``` -//! //! [embedded-hal]: embedded_hal //! [embedded-io]: embedded_io //! [embedded-hal-async]: embedded_hal_async @@ -116,8 +43,8 @@ use core::{marker::PhantomData, sync::atomic::Ordering, task::Poll}; -#[cfg(any(doc, feature = "unstable"))] -use embassy_embedded_hal::SetConfig; +#[cfg(feature = "unstable")] +use embedded_io::ReadExactError; use enumset::{EnumSet, EnumSetType}; use portable_atomic::AtomicBool; @@ -131,7 +58,7 @@ use crate::{ PinGuard, Pull, }, - interrupt::{InterruptConfigurable, InterruptHandler}, + interrupt::InterruptHandler, pac::uart0::RegisterBlock, peripheral::{Peripheral, PeripheralRef}, peripherals::Interrupt, @@ -141,14 +68,11 @@ use crate::{ DriverMode, }; -const UART_FIFO_SIZE: u16 = 128; -const CMD_CHAR_DEFAULT: u8 = 0x2b; - -/// UART Error +/// UART RX Error #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] -pub enum Error { +pub enum RxError { /// The RX FIFO overflow happened. /// /// This error occurs when RX FIFO is full and a new byte is received. @@ -174,32 +98,52 @@ pub enum Error { ParityMismatch, } -impl core::error::Error for Error {} +impl core::error::Error for RxError {} -impl core::fmt::Display for Error { +impl core::fmt::Display for RxError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - Error::FifoOverflowed => write!(f, "The RX FIFO overflowed"), - Error::GlitchOccurred => write!(f, "A glitch was detected on the RX line"), - Error::FrameFormatViolated => write!(f, "A framing error was detected on the RX line"), - Error::ParityMismatch => write!(f, "A parity error was detected on the RX line"), + RxError::FifoOverflowed => write!(f, "The RX FIFO overflowed"), + RxError::GlitchOccurred => write!(f, "A glitch was detected on the RX line"), + RxError::FrameFormatViolated => { + write!(f, "A framing error was detected on the RX line") + } + RxError::ParityMismatch => write!(f, "A parity error was detected on the RX line"), } } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::Error for Error { +#[instability::unstable] +impl embedded_io::Error for RxError { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } +} + +/// UART TX Error +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum TxError {} + +impl core::fmt::Display for TxError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Tx error") + } +} + +#[instability::unstable] +impl embedded_io::Error for TxError { fn kind(&self) -> embedded_io::ErrorKind { embedded_io::ErrorKind::Other } } -// (outside of `config` module in order not to "use" it an extra time) /// UART clock source -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] +#[instability::unstable] pub enum ClockSource { /// APB_CLK clock source #[cfg_attr(not(any(esp32c6, esp32h2, lp_uart)), default)] @@ -216,11 +160,6 @@ pub enum ClockSource { RefTick, } -// see -const UART_FULL_THRESH_DEFAULT: u16 = 120; -// see -const UART_TOUT_THRESH_DEFAULT: u8 = 10; - /// Number of data bits /// /// This enum represents the various configurations for the number of data @@ -230,14 +169,14 @@ const UART_TOUT_THRESH_DEFAULT: u8 = 10; #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DataBits { /// 5 data bits per frame. - _5 = 0, + _5, /// 6 data bits per frame. - _6 = 1, + _6, /// 7 data bits per frame. - _7 = 2, + _7, /// 8 data bits per frame. #[default] - _8 = 3, + _8, } /// Parity check @@ -270,11 +209,26 @@ pub enum Parity { pub enum StopBits { /// 1 stop bit. #[default] - _1 = 1, + _1, /// 1.5 stop bits. - _1p5 = 2, + _1p5, /// 2 stop bits. - _2 = 3, + _2, +} + +/// Defines how strictly the requested baud rate must be met. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[instability::unstable] +pub enum BaudrateTolerance { + /// Accept the closest achievable baud rate without restriction. + #[default] + Closest, + /// In this setting, the deviation of only 1% from the desired baud value is + /// tolerated. + Exact, + /// Allow a certain percentage of deviation. + ErrorPercent(u8), } /// UART Configuration @@ -284,19 +238,52 @@ pub enum StopBits { pub struct Config { /// The baud rate (speed) of the UART communication in bits per second /// (bps). - pub baudrate: u32, + baudrate: u32, + /// Determines how close to the desired baud rate value the driver should + /// set the baud rate. + baudrate_tolerance: BaudrateTolerance, /// Number of data bits in each frame (5, 6, 7, or 8 bits). - pub data_bits: DataBits, + data_bits: DataBits, /// Parity setting (None, Even, or Odd). - pub parity: Parity, + parity: Parity, /// Number of stop bits in each frame (1, 1.5, or 2 bits). - pub stop_bits: StopBits, + stop_bits: StopBits, /// Clock source used by the UART peripheral. - pub clock_source: ClockSource, + #[cfg_attr(not(feature = "unstable"), builder_lite(skip))] + clock_source: ClockSource, /// UART Receive part configuration. - pub rx: RxConfig, + rx: RxConfig, /// UART Transmit part configuration. - pub tx: TxConfig, + tx: TxConfig, +} + +impl Default for Config { + fn default() -> Config { + Config { + rx: RxConfig::default(), + tx: TxConfig::default(), + baudrate: 115_200, + baudrate_tolerance: BaudrateTolerance::default(), + data_bits: Default::default(), + parity: Default::default(), + stop_bits: Default::default(), + clock_source: Default::default(), + } + } +} + +impl Config { + fn validate(&self) -> Result<(), ConfigError> { + if let BaudrateTolerance::ErrorPercent(percentage) = self.baudrate_tolerance { + assert!(percentage > 0 && percentage <= 100); + } + + // Max supported baud rate is 5Mbaud + if self.baudrate == 0 || self.baudrate > 5_000_000 { + return Err(ConfigError::UnsupportedBaudrate); + } + Ok(()) + } } /// UART Receive part configuration. @@ -305,60 +292,37 @@ pub struct Config { #[non_exhaustive] pub struct RxConfig { /// Threshold level at which the RX FIFO is considered full. - pub fifo_full_threshold: u16, + fifo_full_threshold: u16, /// Optional timeout value for RX operations. - pub timeout: Option, + timeout: Option, } -/// UART Transmit part configuration. -#[derive(Debug, Clone, Copy, Default, procmacros::BuilderLite)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] -pub struct TxConfig {} - impl Default for RxConfig { fn default() -> RxConfig { RxConfig { - fifo_full_threshold: UART_FULL_THRESH_DEFAULT, - timeout: Some(UART_TOUT_THRESH_DEFAULT), + // see + fifo_full_threshold: 120, + // see + timeout: Some(10), } } } -impl Default for Config { - fn default() -> Config { - Config { - rx: RxConfig::default(), - tx: TxConfig::default(), - baudrate: 115_200, - data_bits: Default::default(), - parity: Default::default(), - stop_bits: Default::default(), - clock_source: Default::default(), - } - } +/// UART Transmit part configuration. +#[derive(Debug, Clone, Copy, procmacros::BuilderLite)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct TxConfig { + /// Threshold level at which the TX FIFO is considered empty. + fifo_empty_threshold: u16, } -impl Config { - /// Calculates the total symbol length in bits based on the configured - /// data bits, parity, and stop bits. - fn symbol_length(&self) -> u8 { - let mut length: u8 = 1; // start bit - length += match self.data_bits { - DataBits::_5 => 5, - DataBits::_6 => 6, - DataBits::_7 => 7, - DataBits::_8 => 8, - }; - length += match self.parity { - Parity::None => 0, - _ => 1, - }; - length += match self.stop_bits { - StopBits::_1 => 1, - _ => 2, // esp-idf also counts 2 bits for settings 1.5 and 2 stop bits - }; - length +impl Default for TxConfig { + fn default() -> TxConfig { + TxConfig { + // see + fifo_empty_threshold: 10, + } } } @@ -370,17 +334,17 @@ impl Config { pub struct AtCmdConfig { /// Optional idle time before the AT command detection begins, in clock /// cycles. - pub pre_idle_count: Option, + pre_idle_count: Option, /// Optional idle time after the AT command detection ends, in clock /// cycles. - pub post_idle_count: Option, + post_idle_count: Option, /// Optional timeout between characters in the AT command, in clock /// cycles. - pub gap_timeout: Option, + gap_timeout: Option, /// The character that triggers the AT command detection. - pub cmd_char: u8, + cmd_char: u8, /// Optional number of characters to detect as part of the AT command. - pub char_num: u8, + char_num: u8, } impl Default for AtCmdConfig { @@ -389,13 +353,13 @@ impl Default for AtCmdConfig { pre_idle_count: None, post_idle_count: None, gap_timeout: None, - cmd_char: CMD_CHAR_DEFAULT, + cmd_char: b'+', char_num: 1, } } } -struct UartBuilder<'d, Dm> { +struct UartBuilder<'d, Dm: DriverMode> { uart: PeripheralRef<'d, AnyUart>, phantom: PhantomData, } @@ -440,13 +404,28 @@ where } /// UART (Full-duplex) -pub struct Uart<'d, Dm> { +/// +/// ```rust, no_run +#[doc = crate::before_snippet!()] +/// # use esp_hal::uart::{Config, Uart}; +/// let mut uart = Uart::new( +/// peripherals.UART0, +/// Config::default())? +/// .with_rx(peripherals.GPIO1) +/// .with_tx(peripherals.GPIO2); +/// +/// uart.write(b"Hello world!")?; +/// # Ok(()) +/// # } +/// ``` +pub struct Uart<'d, Dm: DriverMode> { rx: UartRx<'d, Dm>, tx: UartTx<'d, Dm>, } /// UART (Transmit) -pub struct UartTx<'d, Dm> { +#[instability::unstable] +pub struct UartTx<'d, Dm: DriverMode> { uart: PeripheralRef<'d, AnyUart>, phantom: PhantomData, guard: PeripheralGuard, @@ -455,7 +434,8 @@ pub struct UartTx<'d, Dm> { } /// UART (Receive) -pub struct UartRx<'d, Dm> { +#[instability::unstable] +pub struct UartRx<'d, Dm: DriverMode> { uart: PeripheralRef<'d, AnyUart>, phantom: PhantomData, guard: PeripheralGuard, @@ -466,10 +446,31 @@ pub struct UartRx<'d, Dm> { #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum ConfigError { - /// The requested timeout is not supported. + /// The requested baud rate is not achievable. + #[cfg(any(doc, feature = "unstable"))] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] + UnachievableBaudrate, + + /// The requested baud rate is not supported. + /// + /// This error is returned if: + /// * the baud rate exceeds 5MBaud or is equal to zero. + /// * the user has specified an exact baud rate or with some percentage of + /// deviation to the desired value, and the driver cannot reach this + /// speed. + UnsupportedBaudrate, + + /// The requested timeout exceeds the maximum value ( + #[cfg_attr(esp32, doc = "127")] + #[cfg_attr(not(esp32), doc = "1023")] + /// ). UnsupportedTimeout, - /// The requested FIFO threshold is not supported. - UnsupportedFifoThreshold, + + /// The requested RX FIFO threshold exceeds the maximum value (127 bytes). + UnsupportedRxFifoThreshold, + + /// The requested TX FIFO threshold exceeds the maximum value (127 bytes). + UnsupportedTxFifoThreshold, } impl core::error::Error for ConfigError {} @@ -477,17 +478,26 @@ impl core::error::Error for ConfigError {} impl core::fmt::Display for ConfigError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { + #[cfg(feature = "unstable")] + ConfigError::UnachievableBaudrate => { + write!(f, "The requested baud rate is not achievable") + } + ConfigError::UnsupportedBaudrate => { + write!(f, "The requested baud rate is not supported") + } ConfigError::UnsupportedTimeout => write!(f, "The requested timeout is not supported"), - ConfigError::UnsupportedFifoThreshold => { - write!(f, "The requested FIFO threshold is not supported") + ConfigError::UnsupportedRxFifoThreshold => { + write!(f, "The requested RX FIFO threshold is not supported") + } + ConfigError::UnsupportedTxFifoThreshold => { + write!(f, "The requested TX FIFO threshold is not supported") } } } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl SetConfig for Uart<'_, Dm> +#[instability::unstable] +impl embassy_embedded_hal::SetConfig for Uart<'_, Dm> where Dm: DriverMode, { @@ -499,9 +509,8 @@ where } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl SetConfig for UartRx<'_, Dm> +#[instability::unstable] +impl embassy_embedded_hal::SetConfig for UartRx<'_, Dm> where Dm: DriverMode, { @@ -513,9 +522,8 @@ where } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl SetConfig for UartTx<'_, Dm> +#[instability::unstable] +impl embassy_embedded_hal::SetConfig for UartTx<'_, Dm> where Dm: DriverMode, { @@ -532,6 +540,7 @@ where Dm: DriverMode, { /// Configure RTS pin + #[instability::unstable] pub fn with_rts(mut self, rts: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(rts); rts.set_to_push_pull_output(); @@ -546,6 +555,7 @@ where /// TX signal. /// /// Disconnects the previous pin that was assigned with `with_tx`. + #[instability::unstable] pub fn with_tx(mut self, tx: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(tx); // Make sure we don't cause an unexpected low pulse on the pin. @@ -558,30 +568,54 @@ where /// Change the configuration. /// - /// Note that this also changes the configuration of the RX half. + /// ## Errors + /// + /// This function returns a [`ConfigError`] if the configuration is not + /// supported by the hardware. #[instability::unstable] - pub fn apply_config(&mut self, _config: &Config) -> Result<(), ConfigError> { - // Nothing to do so far. + pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { + self.uart + .info() + .set_tx_fifo_empty_threshold(config.tx.fifo_empty_threshold)?; self.uart.info().txfifo_reset(); Ok(()) } - /// Writes bytes - pub fn write_bytes(&mut self, data: &[u8]) -> Result { - let count = data.len(); + /// Write bytes. + /// + /// This function writes data to the internal TX FIFO of the UART + /// peripheral. The data is then transmitted over the UART TX line. + /// + /// The function returns the number of bytes written to the FIFO. This may + /// be less than the length of the provided data. The function may only + /// return 0 if the provided data is empty. + /// + /// ## Errors + /// + /// This function returns a [`TxError`] if an error occurred during the + /// write operation. + #[instability::unstable] + pub fn write(&mut self, data: &[u8]) -> Result { + if data.is_empty() { + return Ok(0); + } + + while self.tx_fifo_count() >= Info::UART_FIFO_SIZE {} - for &byte in data { - self.write_byte(byte); + let space = ((Info::UART_FIFO_SIZE - self.tx_fifo_count()) as usize).min(data.len()); + for &byte in &data[..space] { + self.write_byte(byte)?; } - Ok(count) + Ok(space) } - fn write_byte(&mut self, word: u8) { - while self.tx_fifo_count() >= UART_FIFO_SIZE {} + fn write_byte(&mut self, word: u8) -> Result<(), TxError> { self.regs() .fifo() .write(|w| unsafe { w.rxfifo_rd_byte().bits(word) }); + + Ok(()) } #[allow(clippy::useless_conversion)] @@ -591,9 +625,19 @@ where self.regs().status().read().txfifo_cnt().bits().into() } - /// Flush the transmit buffer of the UART - pub fn flush(&mut self) { + /// Flush the transmit buffer. + /// + /// This function blocks until all data in the TX FIFO has been + /// transmitted. + #[instability::unstable] + pub fn flush(&mut self) -> Result<(), TxError> { + while self.tx_fifo_count() > 0 {} + // The FSM is in the Idle state for a short while after the last byte is moved + // out of the FIFO. It is unclear how long this takes, but 10us seems to be a + // good enough duration to wait, for both fast and slow baud rates. + crate::rom::ets_delay_us(10); while !self.is_tx_idle() {} + Ok(()) } /// Checks if the TX line is idle for this UART instance. @@ -637,16 +681,25 @@ where impl<'d> UartTx<'d, Blocking> { /// Create a new UART TX instance in [`Blocking`] mode. + /// + /// ## Errors + /// + /// This function returns a [`ConfigError`] if the configuration is not + /// supported by the hardware. + /// + /// ## Example + /// /// ```rust, no_run #[doc = crate::before_snippet!()] /// # use esp_hal::uart::{Config, UartTx}; /// let tx = UartTx::new( /// peripherals.UART0, - /// Config::default()) - /// .unwrap() + /// Config::default())? /// .with_tx(peripherals.GPIO1); + /// # Ok(()) /// # } /// ``` + #[instability::unstable] pub fn new( uart: impl Peripheral

+ 'd, config: Config, @@ -657,6 +710,7 @@ impl<'d> UartTx<'d, Blocking> { } /// Reconfigures the driver to operate in [`Async`] mode. + #[instability::unstable] pub fn into_async(self) -> UartTx<'d, Async> { if !self.uart.state().is_rx_async.load(Ordering::Acquire) { self.uart @@ -677,6 +731,7 @@ impl<'d> UartTx<'d, Blocking> { impl<'d> UartTx<'d, Async> { /// Reconfigures the driver to operate in [`Blocking`] mode. + #[instability::unstable] pub fn into_blocking(self) -> UartTx<'d, Blocking> { self.uart .state() @@ -725,6 +780,7 @@ where } /// Configure CTS pin + #[instability::unstable] pub fn with_cts(self, cts: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(cts); cts.init_input(Pull::None); @@ -741,6 +797,7 @@ where /// configure the driver side (i.e. the TX pin), or ensure that the line is /// initially high, to avoid receiving a non-data byte caused by an /// initial low signal level. + #[instability::unstable] pub fn with_rx(self, rx: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(rx); rx.init_input(Pull::Up); @@ -751,7 +808,10 @@ where /// Change the configuration. /// - /// Note that this also changes the configuration of the TX half. + /// ## Errors + /// + /// This function returns a [`ConfigError`] if the configuration is not + /// supported by the hardware. #[instability::unstable] pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { self.uart @@ -759,21 +819,21 @@ where .set_rx_fifo_full_threshold(config.rx.fifo_full_threshold)?; self.uart .info() - .set_rx_timeout(config.rx.timeout, config.symbol_length())?; + .set_rx_timeout(config.rx.timeout, self.uart.info().current_symbol_length())?; self.uart.info().rxfifo_reset(); Ok(()) } - /// Reads and clears errors. + /// Reads and clears errors set by received data. #[instability::unstable] - pub fn check_for_errors(&mut self) -> Result<(), Error> { + pub fn check_for_errors(&mut self) -> Result<(), RxError> { let errors = RxEvent::FifoOvf | RxEvent::FifoTout | RxEvent::GlitchDetected | RxEvent::FrameError | RxEvent::ParityError; - let events = self.uart.info().rx_events(errors); + let events = self.uart.info().rx_events().intersection(errors); let result = rx_event_check_for_error(events); if result.is_err() { self.uart.info().clear_rx_events(errors); @@ -781,75 +841,67 @@ where result } - // Read a byte from the UART - fn read_byte(&mut self) -> Option { - cfg_if::cfg_if! { - if #[cfg(esp32s2)] { - // On the ESP32-S2 we need to use PeriBus2 to read the FIFO: - let fifo = unsafe { - &*((self.regs().fifo().as_ptr() as *mut u8).add(0x20C00000) - as *mut crate::pac::uart0::FIFO) - }; - } else { - let fifo = self.regs().fifo(); - } + /// Read bytes. + /// + /// The UART hardware continuously receives bytes and stores them in the RX + /// FIFO. This function reads the bytes from the RX FIFO and returns + /// them in the provided buffer, without blocking. + /// + /// The function returns the number of bytes read into the buffer. This may + /// be less than the length of the buffer. This function only returns 0 + /// if the provided buffer is empty. + /// + /// ## Errors + /// + /// This function returns an [`RxError`] if an error occurred since the last + /// call to [`Self::check_for_errors`], [`Self::read_buffered`], or this + /// function. + /// + /// If the error occurred before this function was called, the contents of + /// the FIFO are not modified. + #[instability::unstable] + pub fn read(&mut self, buf: &mut [u8]) -> Result { + if buf.is_empty() { + return Ok(0); } - if self.rx_fifo_count() > 0 { - // https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/03-errata-description/esp32/cpu-subsequent-access-halted-when-get-interrupted.html - cfg_if::cfg_if! { - if #[cfg(esp32)] { - let byte = crate::interrupt::free(|| fifo.read().rxfifo_rd_byte().bits()); - } else { - let byte = fifo.read().rxfifo_rd_byte().bits(); - } - } - - Some(byte) - } else { - None + while self.rx_fifo_count() == 0 { + // Block until we received at least one byte + self.check_for_errors()?; } + + self.read_buffered(buf) } - /// Reads bytes from the UART - pub fn read_bytes(&mut self, buf: &mut [u8]) -> Result<(), Error> { - let buffered = self.read_buffered_bytes(buf)?; - let buf = &mut buf[buffered..]; + /// Read already received bytes. + /// + /// This function reads the already received bytes from the FIFO into the + /// provided buffer. The function does not wait for the FIFO to actually + /// contain any bytes. + /// + /// The function returns the number of bytes read into the buffer. This may + /// be less than the length of the buffer, and it may also be 0. + /// + /// ## Errors + /// + /// This function returns an [`RxError`] if an error occurred since the last + /// call to [`Self::check_for_errors`], [`Self::read`], or this + /// function. + /// + /// If the error occurred before this function was called, the contents of + /// the FIFO are not modified. + #[instability::unstable] + pub fn read_buffered(&mut self, buf: &mut [u8]) -> Result { + // Get the count first, to avoid accidentally reading a corrupted byte received + // after the error check. + let to_read = (self.rx_fifo_count() as usize).min(buf.len()); + self.check_for_errors()?; - for byte in buf.iter_mut() { - loop { - if let Some(b) = self.read_byte() { - *byte = b; - break; - } - self.check_for_errors()?; - } + for byte_into in buf[..to_read].iter_mut() { + *byte_into = self.uart.info().read_next_from_fifo(); } - Ok(()) - } - /// Read all available bytes from the RX FIFO into the provided buffer and - /// returns the number of read bytes without blocking. - pub fn read_buffered_bytes(&mut self, buf: &mut [u8]) -> Result { - let mut count = 0; - while count < buf.len() { - if let Some(byte) = self.read_byte() { - buf[count] = byte; - count += 1; - } else { - break; - } - } - if let Err(err) = self.check_for_errors() { - // Drain the buffer. We don't know where the error occurred, so returning - // these bytes would be incorrect. We also don't know if the number of buffered - // bytes fit into the buffer. - // TODO: make this behaviour configurable using UART_ERR_WR_MASK. If the user - // wants to keep the bytes regardless of errors, they should be able to do so. - while self.read_byte().is_some() {} - return Err(err); - } - Ok(count) + Ok(to_read) } /// Busy waits for a break condition to be detected on the RX @@ -857,6 +909,7 @@ where /// (i.e. logic 0 for one NULL character transmission) after stop bits. /// /// Clears the break detection interrupt before returning. + #[instability::unstable] pub fn wait_for_break(&mut self) { // Enable the break detection interrupt self.regs().int_ena().write(|w| w.brk_det().bit(true)); @@ -869,27 +922,12 @@ where self.regs().int_clr().write(|w| w.brk_det().bit(true)); } - /// Read bytes from the RX FIFO without checking for errors. - fn flush_buffer(&mut self, buf: &mut [u8]) -> usize { - let mut count = 0; - while count < buf.len() { - if let Some(byte) = self.read_byte() { - buf[count] = byte; - count += 1; - } else { - break; - } - } - count - } - #[allow(clippy::useless_conversion)] fn rx_fifo_count(&self) -> u16 { let fifo_cnt: u16 = self.regs().status().read().rxfifo_cnt().bits().into(); // Calculate the real count based on the FIFO read and write offset address: - // https://www.espressif.com/sites/default/files/documentation/esp32_errata_en.pdf - // section 3.17 + // https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/03-errata-description/esp32/uart-fifo-cnt-indicates-data-length-incorrectly.html #[cfg(esp32)] { let status = self.regs().mem_rx_status().read(); @@ -899,9 +937,9 @@ where if wr_addr > rd_addr { wr_addr - rd_addr } else if wr_addr < rd_addr { - (wr_addr + UART_FIFO_SIZE) - rd_addr + (wr_addr + Info::UART_FIFO_SIZE) - rd_addr } else if fifo_cnt > 0 { - UART_FIFO_SIZE + Info::UART_FIFO_SIZE } else { 0 } @@ -935,16 +973,23 @@ where impl<'d> UartRx<'d, Blocking> { /// Create a new UART RX instance in [`Blocking`] mode. + /// + /// ## Errors + /// + /// This function returns a [`ConfigError`] if the configuration is not + /// supported by the hardware. + /// /// ```rust, no_run #[doc = crate::before_snippet!()] /// # use esp_hal::uart::{Config, UartRx}; /// let rx = UartRx::new( /// peripherals.UART1, - /// Config::default()) - /// .unwrap() + /// Config::default())? /// .with_rx(peripherals.GPIO2); + /// # Ok(()) /// # } /// ``` + #[instability::unstable] pub fn new( uart: impl Peripheral

+ 'd, config: Config, @@ -955,6 +1000,7 @@ impl<'d> UartRx<'d, Blocking> { } /// Reconfigures the driver to operate in [`Async`] mode. + #[instability::unstable] pub fn into_async(self) -> UartRx<'d, Async> { if !self.uart.state().is_tx_async.load(Ordering::Acquire) { self.uart @@ -973,6 +1019,7 @@ impl<'d> UartRx<'d, Blocking> { impl<'d> UartRx<'d, Async> { /// Reconfigures the driver to operate in [`Blocking`] mode. + #[instability::unstable] pub fn into_blocking(self) -> UartRx<'d, Blocking> { self.uart .state() @@ -992,15 +1039,23 @@ impl<'d> UartRx<'d, Async> { impl<'d> Uart<'d, Blocking> { /// Create a new UART instance in [`Blocking`] mode. + /// + /// ## Errors + /// + /// This function returns a [`ConfigError`] if the configuration is not + /// supported by the hardware. + /// + /// ## Example + /// /// ```rust, no_run #[doc = crate::before_snippet!()] /// # use esp_hal::uart::{Config, Uart}; /// let mut uart1 = Uart::new( /// peripherals.UART1, - /// Config::default()) - /// .unwrap() + /// Config::default())? /// .with_rx(peripherals.GPIO1) /// .with_tx(peripherals.GPIO2); + /// # Ok(()) /// # } /// ``` pub fn new( @@ -1099,60 +1154,109 @@ where /// /// This is particularly useful when having two tasks correlating to /// transmitting and receiving. + /// /// ## Example + /// /// ```rust, no_run #[doc = crate::before_snippet!()] /// # use esp_hal::uart::{Config, Uart}; /// # let mut uart1 = Uart::new( /// # peripherals.UART1, - /// # Config::default()) - /// # .unwrap() + /// # Config::default())? /// # .with_rx(peripherals.GPIO1) /// # .with_tx(peripherals.GPIO2); /// // The UART can be split into separate Transmit and Receive components: /// let (mut rx, mut tx) = uart1.split(); /// /// // Each component can be used individually to interact with the UART: - /// tx.write_bytes(&[42u8]).expect("write error!"); + /// tx.write(&[42u8])?; /// let mut byte = [0u8; 1]; - /// rx.read_bytes(&mut byte); + /// rx.read(&mut byte); + /// # Ok(()) /// # } /// ``` + #[instability::unstable] pub fn split(self) -> (UartRx<'d, Dm>, UartTx<'d, Dm>) { (self.rx, self.tx) } - /// Write bytes out over the UART + /// Writes bytes. + /// + /// This function writes data to the internal TX FIFO of the UART + /// peripheral. The data is then transmitted over the UART TX line. + /// + /// The function returns the number of bytes written to the FIFO. This may + /// be less than the length of the provided data. The function may only + /// return 0 if the provided data is empty. + /// + /// ## Errors + /// + /// This function returns a [`TxError`] if an error occurred during the + /// write operation. + /// + /// ## Example + /// /// ```rust, no_run #[doc = crate::before_snippet!()] /// # use esp_hal::uart::{Config, Uart}; /// # let mut uart1 = Uart::new( /// # peripherals.UART1, - /// # Config::default()) - /// # .unwrap(); + /// # Config::default())?; /// // Write bytes out over the UART: - /// uart1.write_bytes(b"Hello, world!").expect("write error!"); + /// uart1.write(b"Hello, world!")?; + /// # Ok(()) /// # } /// ``` - pub fn write_bytes(&mut self, data: &[u8]) -> Result { - self.tx.write_bytes(data) + pub fn write(&mut self, data: &[u8]) -> Result { + self.tx.write(data) } /// Reads and clears errors set by received data. #[instability::unstable] - pub fn check_for_rx_errors(&mut self) -> Result<(), Error> { + pub fn check_for_rx_errors(&mut self) -> Result<(), RxError> { self.rx.check_for_errors() } - /// Reads bytes from the UART - pub fn read_bytes(&mut self, buf: &mut [u8]) -> Result<(), Error> { - self.rx.read_bytes(buf) + /// Read received bytes. + /// + /// The UART hardware continuously receives bytes and stores them in the RX + /// FIFO. This function reads the bytes from the RX FIFO and returns + /// them in the provided buffer, without blocking. + /// + /// The function returns the number of bytes read into the buffer. This may + /// be less than the length of the buffer. This function only returns 0 + /// if the provided buffer is empty. + /// + /// ## Errors + /// + /// This function returns an [`RxError`] if an error occurred since the last + /// check for errors. + /// + /// If the error occurred before this function was called, the contents of + /// the FIFO are not modified. + pub fn read(&mut self, buf: &mut [u8]) -> Result { + self.rx.read(buf) } - /// Read all available bytes from the RX FIFO into the provided buffer and - /// returns the number of read bytes without blocking. - pub fn read_buffered_bytes(&mut self, buf: &mut [u8]) -> Result { - self.rx.read_buffered_bytes(buf) + /// Read already received bytes. + /// + /// This function reads the already received bytes from the FIFO into the + /// provided buffer. The function does not wait for the FIFO to actually + /// contain any bytes. + /// + /// The function returns the number of bytes read into the buffer. This may + /// be less than the length of the buffer, and it may also be 0. + /// + /// ## Errors + /// + /// This function returns an [`RxError`] if an error occurred since the last + /// check for errors. + /// + /// If the error occurred before this function was called, the contents of + /// the FIFO are not modified. + #[instability::unstable] + pub fn read_buffered(&mut self, buf: &mut [u8]) -> Result { + self.rx.read_buffered(buf) } /// Configures the AT-CMD detection settings @@ -1198,15 +1302,22 @@ where } /// Flush the transmit buffer of the UART - pub fn flush(&mut self) { + pub fn flush(&mut self) -> Result<(), TxError> { self.tx.flush() } /// Change the configuration. + /// + /// ## Errors + /// + /// This function returns a [`ConfigError`] if the configuration is not + /// supported by the hardware. pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { + // Must apply the common settings first, as `rx.apply_config` reads back symbol + // size. + self.rx.uart.info().apply_config(config)?; self.rx.apply_config(config)?; self.tx.apply_config(config)?; - self.rx.uart.info().apply_config(config)?; Ok(()) } @@ -1233,6 +1344,10 @@ where self.apply_config(&config)?; + // Reset Tx/Rx FIFOs + self.rx.uart.info().rxfifo_reset(); + self.rx.uart.info().txfifo_reset(); + // Don't wait after transmissions by default, // so that bytes written to TX FIFO are always immediately transmitted. self.regs() @@ -1287,7 +1402,8 @@ where impl crate::private::Sealed for Uart<'_, Blocking> {} -impl InterruptConfigurable for Uart<'_, Blocking> { +#[instability::unstable] +impl crate::interrupt::InterruptConfigurable for Uart<'_, Blocking> { fn set_interrupt_handler(&mut self, handler: InterruptHandler) { // `self.tx.uart` and `self.rx.uart` are the same self.tx.uart.info().set_interrupt_handler(handler); @@ -1316,6 +1432,70 @@ impl Uart<'_, Blocking> { } /// Listen for the given interrupts + /// + /// ## Example + /// + /// **Note**: In practice a proper serial terminal should be used + /// to connect to the board (espflash won't work) + /// + /// ```rust, no_run + #[doc = crate::before_snippet!()] + /// # use esp_hal::delay::Delay; + /// # use esp_hal::uart::{AtCmdConfig, Config, RxConfig, Uart, UartInterrupt}; + /// # let delay = Delay::new(); + /// # let config = Config::default().with_rx( + /// # RxConfig::default().with_fifo_full_threshold(30) + /// # ); + /// # let mut uart0 = Uart::new( + /// # peripherals.UART0, + /// # config)?; + /// uart0.set_interrupt_handler(interrupt_handler); + /// + /// critical_section::with(|cs| { + /// uart0.set_at_cmd(AtCmdConfig::default().with_cmd_char(b'#')); + /// uart0.listen(UartInterrupt::AtCmd | UartInterrupt::RxFifoFull); + /// + /// SERIAL.borrow_ref_mut(cs).replace(uart0); + /// }); + /// + /// loop { + /// println!("Send `#` character or >=30 characters"); + /// delay.delay(Duration::from_secs(1)); + /// } + /// # } + /// + /// # use core::cell::RefCell; + /// # use critical_section::Mutex; + /// # use esp_hal::uart::Uart; + /// static SERIAL: Mutex>>> = + /// Mutex::new(RefCell::new(None)); + /// + /// # use esp_hal::uart::UartInterrupt; + /// # use core::fmt::Write; + /// #[handler] + /// fn interrupt_handler() { + /// critical_section::with(|cs| { + /// let mut serial = SERIAL.borrow_ref_mut(cs); + /// if let Some(serial) = serial.as_mut() { + /// let mut buf = [0u8; 64]; + /// if let Ok(cnt) = serial.read_buffered(&mut buf) { + /// println!("Read {} bytes", cnt); + /// } + /// + /// let pending_interrupts = serial.interrupts(); + /// println!( + /// "Interrupt AT-CMD: {} RX-FIFO-FULL: {}", + /// pending_interrupts.contains(UartInterrupt::AtCmd), + /// pending_interrupts.contains(UartInterrupt::RxFifoFull), + /// ); + /// + /// serial.clear_interrupts( + /// UartInterrupt::AtCmd | UartInterrupt::RxFifoFull + /// ); + /// } + /// }); + /// } + /// ``` #[instability::unstable] pub fn listen(&mut self, interrupts: impl Into>) { self.tx.uart.info().enable_listen(interrupts.into(), true) @@ -1340,11 +1520,12 @@ impl Uart<'_, Blocking> { } } +#[instability::unstable] impl ufmt_write::uWrite for Uart<'_, Dm> where Dm: DriverMode, { - type Error = Error; + type Error = TxError; #[inline] fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { @@ -1357,15 +1538,16 @@ where } } +#[instability::unstable] impl ufmt_write::uWrite for UartTx<'_, Dm> where Dm: DriverMode, { - type Error = Error; + type Error = TxError; #[inline] fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { - self.write_bytes(s.as_bytes())?; + self.write(s.as_bytes())?; Ok(()) } } @@ -1386,73 +1568,103 @@ where { #[inline] fn write_str(&mut self, s: &str) -> core::fmt::Result { - self.write_bytes(s.as_bytes()) - .map_err(|_| core::fmt::Error)?; + self.write(s.as_bytes()).map_err(|_| core::fmt::Error)?; Ok(()) } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::ErrorType for Uart<'_, Dm> { - type Error = Error; +/// UART Tx or Rx Error +#[instability::unstable] +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum IoError { + /// UART TX error + Tx(TxError), + /// UART RX error + Rx(RxError), } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::ErrorType for UartTx<'_, Dm> { - type Error = Error; +#[instability::unstable] +impl core::error::Error for IoError {} + +#[instability::unstable] +impl core::fmt::Display for IoError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + IoError::Tx(e) => e.fmt(f), + IoError::Rx(e) => e.fmt(f), + } + } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::ErrorType for UartRx<'_, Dm> { - type Error = Error; +#[instability::unstable] +impl embedded_io::Error for IoError { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::Read for Uart<'_, Dm> -where - Dm: DriverMode, -{ - fn read(&mut self, buf: &mut [u8]) -> Result { - self.rx.read(buf) +#[instability::unstable] +impl From for IoError { + fn from(e: RxError) -> Self { + IoError::Rx(e) } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] -impl embedded_io::Read for UartRx<'_, Dm> -where - Dm: DriverMode, -{ - fn read(&mut self, buf: &mut [u8]) -> Result { - if buf.is_empty() { - return Ok(0); - } +#[instability::unstable] +impl From for IoError { + fn from(e: TxError) -> Self { + IoError::Tx(e) + } +} - while self.rx_fifo_count() == 0 { - // Block until we received at least one byte - } +#[instability::unstable] +impl embedded_io::ErrorType for Uart<'_, Dm> { + type Error = IoError; +} + +#[instability::unstable] +impl embedded_io::ErrorType for UartTx<'_, Dm> { + type Error = TxError; +} + +#[instability::unstable] +impl embedded_io::ErrorType for UartRx<'_, Dm> { + type Error = RxError; +} + +#[instability::unstable] +impl embedded_io::Read for Uart<'_, Dm> +where + Dm: DriverMode, +{ + fn read(&mut self, buf: &mut [u8]) -> Result { + self.rx.read(buf).map_err(IoError::Rx) + } +} - self.read_buffered_bytes(buf) +#[instability::unstable] +impl embedded_io::Read for UartRx<'_, Dm> +where + Dm: DriverMode, +{ + fn read(&mut self, buf: &mut [u8]) -> Result { + self.read(buf) } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] +#[instability::unstable] impl embedded_io::ReadReady for Uart<'_, Dm> where Dm: DriverMode, { fn read_ready(&mut self) -> Result { - self.rx.read_ready() + self.rx.read_ready().map_err(IoError::Rx) } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] +#[instability::unstable] impl embedded_io::ReadReady for UartRx<'_, Dm> where Dm: DriverMode, @@ -1462,34 +1674,31 @@ where } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] +#[instability::unstable] impl embedded_io::Write for Uart<'_, Dm> where Dm: DriverMode, { fn write(&mut self, buf: &[u8]) -> Result { - self.tx.write(buf) + self.tx.write(buf).map_err(IoError::Tx) } fn flush(&mut self) -> Result<(), Self::Error> { - embedded_io::Write::flush(&mut self.tx) + self.tx.flush().map_err(IoError::Tx) } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] +#[instability::unstable] impl embedded_io::Write for UartTx<'_, Dm> where Dm: DriverMode, { fn write(&mut self, buf: &[u8]) -> Result { - self.write_bytes(buf) + self.write(buf) } fn flush(&mut self) -> Result<(), Self::Error> { - self.flush(); - Ok(()) + self.flush() } } @@ -1511,17 +1720,14 @@ pub(crate) enum RxEvent { ParityError, } -fn rx_event_check_for_error(events: EnumSet) -> Result<(), Error> { +fn rx_event_check_for_error(events: EnumSet) -> Result<(), RxError> { for event in events { match event { - RxEvent::FifoOvf => return Err(Error::FifoOverflowed), - RxEvent::GlitchDetected => return Err(Error::GlitchOccurred), - RxEvent::FrameError => return Err(Error::FrameFormatViolated), - RxEvent::ParityError => return Err(Error::ParityMismatch), - RxEvent::FifoFull - | RxEvent::BreakDetected - | RxEvent::CmdCharDetected - | RxEvent::FifoTout => continue, + RxEvent::FifoOvf => return Err(RxError::FifoOverflowed), + RxEvent::GlitchDetected => return Err(RxError::GlitchOccurred), + RxEvent::FrameError => return Err(RxError::FrameFormatViolated), + RxEvent::ParityError => return Err(RxError::ParityMismatch), + RxEvent::FifoFull | RxEvent::BreakDetected | RxEvent::CmdCharDetected | RxEvent::FifoTout => continue, } } @@ -1560,15 +1766,16 @@ impl core::future::Future for UartRxFuture { mut self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, ) -> core::task::Poll { - if !self.registered { - self.state.rx_waker.register(cx.waker()); - self.uart.enable_listen_rx(self.events, true); - self.registered = true; - } - let events = self.uart.enabled_rx_events(self.events); + let events = self.uart.rx_events().intersection(self.events); if !events.is_empty() { + self.uart.clear_rx_events(events); Poll::Ready(events) } else { + self.state.rx_waker.register(cx.waker()); + if !self.registered { + self.uart.enable_listen_rx(self.events, true); + self.registered = true; + } Poll::Pending } } @@ -1601,30 +1808,6 @@ impl UartTxFuture { registered: false, } } - - fn triggered_events(&self) -> bool { - let interrupts_enabled = self.uart.regs().int_ena().read(); - let mut event_triggered = false; - for event in self.events { - event_triggered |= match event { - TxEvent::Done => interrupts_enabled.tx_done().bit_is_clear(), - TxEvent::FiFoEmpty => interrupts_enabled.txfifo_empty().bit_is_clear(), - } - } - event_triggered - } - - fn enable_listen(&self, enable: bool) { - self.uart.regs().int_ena().modify(|_, w| { - for event in self.events { - match event { - TxEvent::Done => w.tx_done().bit(enable), - TxEvent::FiFoEmpty => w.txfifo_empty().bit(enable), - }; - } - w - }); - } } impl core::future::Future for UartTxFuture { @@ -1634,15 +1817,16 @@ impl core::future::Future for UartTxFuture { mut self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, ) -> core::task::Poll { - if !self.registered { - self.state.tx_waker.register(cx.waker()); - self.enable_listen(true); - self.registered = true; - } - - if self.triggered_events() { + let events = self.uart.tx_events().intersection(self.events); + if !events.is_empty() { + self.uart.clear_tx_events(events); Poll::Ready(()) } else { + self.state.tx_waker.register(cx.waker()); + if !self.registered { + self.uart.enable_listen_tx(self.events, true); + self.registered = true; + } Poll::Pending } } @@ -1653,14 +1837,30 @@ impl Drop for UartTxFuture { // Although the isr disables the interrupt that occurred directly, we need to // disable the other interrupts (= the ones that did not occur), as // soon as this future goes out of scope. - self.enable_listen(false); + self.uart.enable_listen_tx(self.events, false); } } impl Uart<'_, Async> { - /// Asynchronously reads data from the UART receive buffer into the - /// provided buffer. - pub async fn read_async(&mut self, buf: &mut [u8]) -> Result { + /// Read data asynchronously. + /// + /// This function reads data from the UART receive buffer into the + /// provided buffer. If the buffer is empty, the function waits + /// asynchronously for data to become available, or for an error to occur. + /// + /// The function returns the number of bytes read into the buffer. This may + /// be less than the length of the buffer. + /// + /// Note that this function may ignore the `rx_fifo_full_threshold` setting + /// to ensure that it does not wait for more data than the buffer can hold. + /// + /// Upon an error, the function returns immediately and the contents of the + /// internal FIFO are not modified. + /// + /// ## Cancellation + /// + /// This function is cancellation safe. + pub async fn read_async(&mut self, buf: &mut [u8]) -> Result { self.rx.read_async(buf).await } @@ -1671,57 +1871,88 @@ impl Uart<'_, Async> { self.rx.wait_for_break_async().await; } - /// Asynchronously writes data to the UART transmit buffer. - pub async fn write_async(&mut self, words: &[u8]) -> Result { + /// Write data into the TX buffer. + /// + /// This function writes the provided buffer `bytes` into the UART transmit + /// buffer. If the buffer is full, the function waits asynchronously for + /// space in the buffer to become available. + /// + /// The function returns the number of bytes written into the buffer. This + /// may be less than the length of the buffer. + /// + /// Upon an error, the function returns immediately and the contents of the + /// internal FIFO are not modified. + /// + /// ## Cancellation + /// + /// This function is cancellation safe. + pub async fn write_async(&mut self, words: &[u8]) -> Result { self.tx.write_async(words).await } /// Asynchronously flushes the UART transmit buffer. - pub async fn flush_async(&mut self) -> Result<(), Error> { + /// + /// This function ensures that all pending data in the transmit FIFO has + /// been sent over the UART. If the FIFO contains data, it waits for the + /// transmission to complete before returning. + /// + /// ## Cancellation + /// + /// This function is cancellation safe. + pub async fn flush_async(&mut self) -> Result<(), TxError> { self.tx.flush_async().await } } impl UartTx<'_, Async> { - /// Asynchronously writes data to the UART transmit buffer in chunks. - /// - /// This function sends the contents of the provided buffer `words` over - /// the UART. Data is written in chunks to avoid overflowing the - /// transmit FIFO, and the function waits asynchronously when - /// necessary for space in the buffer to become available. - pub async fn write_async(&mut self, words: &[u8]) -> Result { - let mut count = 0; - let mut offset: usize = 0; - loop { - let mut next_offset = offset + (UART_FIFO_SIZE - self.tx_fifo_count()) as usize; - if next_offset > words.len() { - next_offset = words.len(); - } - - for byte in &words[offset..next_offset] { - self.write_byte(*byte); - count += 1; + /// Write data into the TX buffer. + /// + /// This function writes the provided buffer `bytes` into the UART transmit + /// buffer. If the buffer is full, the function waits asynchronously for + /// space in the buffer to become available. + /// + /// The function returns the number of bytes written into the buffer. This + /// may be less than the length of the buffer. + /// + /// Upon an error, the function returns immediately and the contents of the + /// internal FIFO are not modified. + /// + /// ## Cancellation + /// + /// This function is cancellation safe. + pub async fn write_async(&mut self, bytes: &[u8]) -> Result { + // We need to loop in case the TX empty interrupt was fired but not cleared + // before, but the FIFO itself was filled up by a previous write. + let space = loop { + let space = Info::UART_FIFO_SIZE - self.tx_fifo_count(); + if space != 0 { + break space; } + UartTxFuture::new(self.uart.reborrow(), TxEvent::FiFoEmpty).await; + }; - if next_offset >= words.len() { - break; - } + let free = (space as usize).min(bytes.len()); - offset = next_offset; - UartTxFuture::new(self.uart.reborrow(), TxEvent::FiFoEmpty).await; + for &byte in &bytes[..free] { + self.regs() + .fifo() + .write(|w| unsafe { w.rxfifo_rd_byte().bits(byte) }); } - Ok(count) + Ok(free) } /// Asynchronously flushes the UART transmit buffer. /// /// This function ensures that all pending data in the transmit FIFO has - /// been sent over the UART. If the FIFO contains data, it waits - /// for the transmission to complete before returning. - pub async fn flush_async(&mut self) -> Result<(), Error> { - let count = self.tx_fifo_count(); - if count > 0 { + /// been sent over the UART. If the FIFO contains data, it waits for the + /// transmission to complete before returning. + /// + /// ## Cancellation + /// + /// This function is cancellation safe. + pub async fn flush_async(&mut self) -> Result<(), TxError> { + if self.tx_fifo_count() > 0 { UartTxFuture::new(self.uart.reborrow(), TxEvent::Done).await; } @@ -1730,33 +1961,34 @@ impl UartTx<'_, Async> { } impl UartRx<'_, Async> { - /// Read async to buffer slice `buf`. - /// Waits until at least one byte is in the Rx FiFo - /// and one of the following interrupts occurs: - /// - `RXFIFO_FULL` - /// - `RXFIFO_OVF` - /// - `AT_CMD_CHAR_DET` (only if `set_at_cmd` was called) - /// - `RXFIFO_TOUT` (only if `set_rx_timeout` was called) - /// - /// The interrupts in question are enabled during the body of this - /// function. The method immediately returns when the interrupt - /// has already occurred before calling this method (e.g. status - /// bit set, but interrupt not enabled) - /// - /// # Params - /// - `buf` buffer slice to write the bytes into - /// - /// - /// # Ok - /// When successful, returns the number of bytes written to buf. - /// If the passed in buffer is of length 0, Ok(0) is returned. - pub async fn read_async(&mut self, buf: &mut [u8]) -> Result { - if buf.is_empty() { - return Ok(0); - } + async fn wait_for_buffered_data( + &mut self, + minimum: usize, + preferred: usize, + listen_for_timeout: bool, + ) -> Result<(), RxError> { + while self.rx_fifo_count() < (minimum as u16).min(Info::RX_FIFO_MAX_THRHD) { + let amount = u16::try_from(preferred) + .unwrap_or(Info::RX_FIFO_MAX_THRHD) + .min(Info::RX_FIFO_MAX_THRHD); + + let current = self.uart.info().rx_fifo_full_threshold(); + let _guard = if current > amount { + // We're ignoring the user configuration here to ensure that this is not waiting + // for more data than the buffer. We'll restore the original value after the + // future resolved. + let info = self.uart.info(); + unwrap!(info.set_rx_fifo_full_threshold(amount)); + Some(OnDrop::new(|| { + unwrap!(info.set_rx_fifo_full_threshold(current)); + })) + } else { + None + }; - loop { + // Wait for space or event let mut events = RxEvent::FifoFull + | RxEvent::BreakDetected | RxEvent::FifoOvf | RxEvent::FrameError | RxEvent::GlitchDetected @@ -1773,27 +2005,72 @@ impl UartRx<'_, Async> { let reg_en = self.regs().conf1(); } }; - if reg_en.read().rx_tout_en().bit_is_set() { + if listen_for_timeout && reg_en.read().rx_tout_en().bit_is_set() { events |= RxEvent::FifoTout; } - let events_happened = UartRxFuture::new(self.uart.reborrow(), events).await; - // always drain the fifo, if an error has occurred the data is lost - let read_bytes = self.flush_buffer(buf); - // check error events - rx_event_check_for_error(events_happened)?; - // 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 - // cleared. Since we do not drain the fifo in the interrupt handler, we need to - // reset the counter here, after draining the fifo. - self.regs() - .int_clr() - .write(|w| w.rxfifo_tout().clear_bit_by_one()); + let event = UartRxFuture::new(self.uart.reborrow(), events).await; + rx_event_check_for_error(event)?; + } - if read_bytes > 0 { - return Ok(read_bytes); - } + Ok(()) + } + + /// Read data asynchronously. + /// + /// This function reads data from the UART receive buffer into the + /// provided buffer. If the buffer is empty, the function waits + /// asynchronously for data to become available, or for an error to occur. + /// + /// The function returns the number of bytes read into the buffer. This may + /// be less than the length of the buffer. + /// + /// Note that this function may ignore the `rx_fifo_full_threshold` setting + /// to ensure that it does not wait for more data than the buffer can hold. + /// + /// Upon an error, the function returns immediately and the contents of the + /// internal FIFO are not modified. + /// + /// ## Cancellation + /// + /// This function is cancellation safe. + pub async fn read_async(&mut self, buf: &mut [u8]) -> Result { + if buf.is_empty() { + return Ok(0); } + + self.wait_for_buffered_data(1, buf.len(), true).await?; + + self.read_buffered(buf) + } + + /// Fill buffer asynchronously. + /// + /// This function reads data into the provided buffer. If the internal FIFO + /// does not contain enough data, the function waits asynchronously for data + /// to become available, or for an error to occur. + /// + /// Note that this function may ignore the `rx_fifo_full_threshold` setting + /// to ensure that it does not wait for more data than the buffer can hold. + /// + /// ## Cancellation + /// + /// This function is **not** cancellation safe. If the future is dropped + /// before it resolves, or if an error occurs during the read operation, + /// previously read data may be lost. + pub async fn read_exact_async(&mut self, mut buf: &mut [u8]) -> Result<(), RxError> { + while !buf.is_empty() { + // No point in listening for timeouts, as we're waiting for an exact amount of + // data. On ESP32 and S2, the timeout interrupt can't be cleared unless the FIFO + // is empty, so listening could cause an infinite loop here. + self.wait_for_buffered_data(buf.len(), buf.len(), false) + .await?; + + let read = self.read_buffered(buf)?; + buf = &mut buf[read..]; + } + + Ok(()) } /// Interrupt-driven wait for a break condition on the RX line. @@ -1804,42 +2081,44 @@ impl UartRx<'_, Async> { } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] +#[instability::unstable] impl embedded_io_async::Read for Uart<'_, Async> { - /// In contrast to the documentation of embedded_io_async::Read, this - /// method blocks until an uart interrupt occurs. - /// See UartRx::read_async for more details. async fn read(&mut self, buf: &mut [u8]) -> Result { - self.read_async(buf).await + self.read_async(buf).await.map_err(IoError::Rx) + } + + async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ReadExactError> { + self.read_exact_async(buf) + .await + .map_err(|e| ReadExactError::Other(IoError::Rx(e))) } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] +#[instability::unstable] impl embedded_io_async::Read for UartRx<'_, Async> { - /// In contrast to the documentation of embedded_io_async::Read, this - /// method blocks until an uart interrupt occurs. - /// See UartRx::read_async for more details. async fn read(&mut self, buf: &mut [u8]) -> Result { self.read_async(buf).await } + + async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ReadExactError> { + self.read_exact_async(buf) + .await + .map_err(ReadExactError::Other) + } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] +#[instability::unstable] impl embedded_io_async::Write for Uart<'_, Async> { async fn write(&mut self, buf: &[u8]) -> Result { - self.write_async(buf).await + self.write_async(buf).await.map_err(IoError::Tx) } async fn flush(&mut self) -> Result<(), Self::Error> { - self.flush_async().await + self.flush_async().await.map_err(IoError::Tx) } } -#[cfg(any(doc, feature = "unstable"))] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] +#[instability::unstable] impl embedded_io_async::Write for UartTx<'_, Async> { async fn write(&mut self, buf: &[u8]) -> Result { self.write_async(buf).await @@ -1858,17 +2137,14 @@ pub(super) fn intr_handler(uart: &Info, state: &State) { let interrupts = uart.regs().int_st().read(); let interrupt_bits = interrupts.bits(); // = int_raw & int_ena let rx_wake = interrupts.rxfifo_full().bit_is_set() - || interrupts.rxfifo_ovf().bit_is_set() - || interrupts.rxfifo_tout().bit_is_set() - || interrupts.brk_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(); - uart.regs() - .int_clr() - .write(|w| unsafe { w.bits(interrupt_bits) }); + | interrupts.rxfifo_ovf().bit_is_set() + | interrupts.rxfifo_tout().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(); + uart.regs() .int_ena() .modify(|r, w| unsafe { w.bits(r.bits() & !interrupt_bits) }); @@ -1899,6 +2175,12 @@ pub mod lp_uart { impl LpUart { /// Initialize the UART driver using the provided configuration + /// + /// # Panics + /// + /// [`Apb`] is a wrong clock source for LP_UART + /// + /// [`Apb`]: super::ClockSource::Apb // TODO: CTS and RTS pins pub fn new( uart: LP_UART, @@ -1948,7 +2230,7 @@ pub mod lp_uart { // Override protocol parameters from the configuration // uart_hal_set_baudrate(&hal, cfg->uart_proto_cfg.baud_rate, sclk_freq); - me.change_baud_internal(config.baudrate, config.clock_source); + me.change_baud_internal(&config); // uart_hal_set_parity(&hal, cfg->uart_proto_cfg.parity); me.change_parity(config.parity); // uart_hal_set_data_bit_num(&hal, cfg->uart_proto_cfg.data_bits); @@ -2003,17 +2285,17 @@ pub mod lp_uart { } } - fn change_baud_internal(&mut self, baudrate: u32, clock_source: super::ClockSource) { + fn change_baud_internal(&mut self, config: &Config) { // TODO: Currently it's not possible to use XtalD2Clk let clk = 16_000_000_u32; let max_div = 0b1111_1111_1111 - 1; - let clk_div = clk.div_ceil(max_div * baudrate); + let clk_div = clk.div_ceil(max_div * config.baudrate); self.uart.register_block().clk_conf().modify(|_, w| unsafe { w.sclk_div_a().bits(0); w.sclk_div_b().bits(0); w.sclk_div_num().bits(clk_div as u8 - 1); - w.sclk_sel().bits(match clock_source { + w.sclk_sel().bits(match config.clock_source { super::ClockSource::Xtal => 3, super::ClockSource::RcFast => 2, super::ClockSource::Apb => panic!("Wrong clock source for LP_UART"), @@ -2022,7 +2304,7 @@ pub mod lp_uart { }); let clk = clk / clk_div; - let divider = clk / baudrate; + let divider = clk / config.baudrate; let divider = divider as u16; self.uart @@ -2034,8 +2316,14 @@ pub mod lp_uart { } /// Modify UART baud rate and reset TX/RX fifo. - pub fn change_baud(&mut self, baudrate: u32, clock_source: super::ClockSource) { - self.change_baud_internal(baudrate, clock_source); + /// + /// # Panics + /// + /// [`Apb`] is a wrong clock source for LP_UART + /// + /// [`Apb`]: super::ClockSource::Apb + pub fn change_baud(&mut self, config: &Config) { + self.change_baud_internal(config); self.txfifo_reset(); self.rxfifo_reset(); } @@ -2067,7 +2355,6 @@ pub mod lp_uart { .modify(|_, w| unsafe { w.bit_num().bits(data_bits as u8) }); self.update(); - self } @@ -2075,7 +2362,7 @@ pub mod lp_uart { self.uart .register_block() .conf0() - .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) }); + .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8 + 1) }); self.update(); self @@ -2161,6 +2448,12 @@ pub struct State { } impl Info { + // Currently we don't support merging adjacent FIFO memory, so the max size is + // 128 bytes, the max threshold is 127 bytes. + const UART_FIFO_SIZE: u16 = 128; + const RX_FIFO_MAX_THRHD: u16 = 127; + const TX_FIFO_MAX_THRHD: u16 = Self::RX_FIFO_MAX_THRHD; + /// Returns the register block for this UART instance. pub fn regs(&self) -> &RegisterBlock { unsafe { &*self.register_block } @@ -2176,7 +2469,7 @@ impl Info { UartInterrupt::AtCmd => w.at_cmd_char_det().bit(enable), UartInterrupt::TxDone => w.tx_done().bit(enable), UartInterrupt::RxFifoFull => w.rxfifo_full().bit(enable), - UartInterrupt::RxBreakDetected => w.brk_det().bit(enable), + UartInterrupt::BreakDetected => w.brk_det().bit(enable), }; } w @@ -2199,7 +2492,7 @@ impl Info { res.insert(UartInterrupt::RxFifoFull); } if ints.brk_det().bit_is_set() { - res.insert(UartInterrupt::RxBreakDetected); + res.insert(UartInterrupt::BreakDetected); } res @@ -2214,7 +2507,7 @@ impl Info { UartInterrupt::AtCmd => w.at_cmd_char_det().clear_bit_by_one(), UartInterrupt::TxDone => w.tx_done().clear_bit_by_one(), UartInterrupt::RxFifoFull => w.rxfifo_full().clear_bit_by_one(), - UartInterrupt::RxBreakDetected => w.brk_det().clear_bit_by_one(), + UartInterrupt::BreakDetected => w.brk_det().clear_bit_by_one(), }; } w @@ -2222,7 +2515,7 @@ impl Info { } fn set_interrupt_handler(&self, handler: InterruptHandler) { - for core in crate::Cpu::other() { + for core in crate::system::Cpu::other() { crate::interrupt::disable(core, self.interrupt); } self.enable_listen(EnumSet::all(), false); @@ -2232,28 +2525,63 @@ impl Info { } fn disable_interrupts(&self) { - crate::interrupt::disable(crate::Cpu::current(), self.interrupt); + crate::interrupt::disable(crate::system::Cpu::current(), self.interrupt); } fn apply_config(&self, config: &Config) -> Result<(), ConfigError> { - self.change_baud(config.baudrate, config.clock_source); + config.validate()?; + self.change_baud(config)?; self.change_data_bits(config.data_bits); self.change_parity(config.parity); self.change_stop_bits(config.stop_bits); - // Reset Tx/Rx FIFOs - self.rxfifo_reset(); - self.txfifo_reset(); - Ok(()) } + fn enable_listen_tx(&self, events: EnumSet, enable: bool) { + self.regs().int_ena().modify(|_, w| { + for event in events { + match event { + TxEvent::Done => w.tx_done().bit(enable), + TxEvent::FiFoEmpty => w.txfifo_empty().bit(enable), + }; + } + w + }); + } + + fn tx_events(&self) -> EnumSet { + let pending_interrupts = self.regs().int_raw().read(); + let mut active_events = EnumSet::new(); + + if pending_interrupts.tx_done().bit_is_set() { + active_events |= TxEvent::Done; + } + if pending_interrupts.txfifo_empty().bit_is_set() { + active_events |= TxEvent::FiFoEmpty; + } + + active_events + } + + fn clear_tx_events(&self, events: impl Into>) { + let events = events.into(); + self.regs().int_clr().write(|w| { + for event in events { + match event { + TxEvent::FiFoEmpty => w.txfifo_empty().clear_bit_by_one(), + TxEvent::Done => w.tx_done().clear_bit_by_one(), + }; + } + w + }); + } + fn enable_listen_rx(&self, events: EnumSet, enable: bool) { self.regs().int_ena().modify(|_, w| { for event in events { match event { RxEvent::FifoFull => w.rxfifo_full().bit(enable), - RxEvent::BreakDetected => w.brk_det().bit(enable), RxEvent::CmdCharDetected => w.at_cmd_char_det().bit(enable), RxEvent::FifoOvf => w.rxfifo_ovf().bit(enable), @@ -2267,50 +2595,36 @@ impl Info { }); } - fn enabled_rx_events(&self, events: impl Into>) -> EnumSet { - let events = events.into(); - let interrupts_enabled = self.regs().int_ena().read(); - let mut events_triggered = EnumSet::new(); - for event in events { - let event_triggered = match event { - RxEvent::FifoFull => interrupts_enabled.rxfifo_full().bit_is_clear(), - RxEvent::BreakDetected => interrupts_enabled.brk_det().bit_is_clear(), - RxEvent::CmdCharDetected => interrupts_enabled.at_cmd_char_det().bit_is_clear(), - - RxEvent::FifoOvf => interrupts_enabled.rxfifo_ovf().bit_is_clear(), - RxEvent::FifoTout => interrupts_enabled.rxfifo_tout().bit_is_clear(), - RxEvent::GlitchDetected => interrupts_enabled.glitch_det().bit_is_clear(), - RxEvent::FrameError => interrupts_enabled.frm_err().bit_is_clear(), - RxEvent::ParityError => interrupts_enabled.parity_err().bit_is_clear(), - }; - if event_triggered { - events_triggered |= event; - } - } - events_triggered - } + fn rx_events(&self) -> EnumSet { + let pending_interrupts = self.regs().int_raw().read(); + let mut active_events = EnumSet::new(); - fn rx_events(&self, events: impl Into>) -> EnumSet { - let events = events.into(); - let interrupts_enabled = self.regs().int_st().read(); - let mut events_triggered = EnumSet::new(); - for event in events { - let event_triggered = match event { - RxEvent::FifoFull => interrupts_enabled.rxfifo_full().bit_is_set(), - RxEvent::BreakDetected => interrupts_enabled.brk_det().bit_is_set(), - RxEvent::CmdCharDetected => interrupts_enabled.at_cmd_char_det().bit_is_set(), - - RxEvent::FifoOvf => interrupts_enabled.rxfifo_ovf().bit_is_set(), - RxEvent::FifoTout => interrupts_enabled.rxfifo_tout().bit_is_set(), - RxEvent::GlitchDetected => interrupts_enabled.glitch_det().bit_is_set(), - RxEvent::FrameError => interrupts_enabled.frm_err().bit_is_set(), - RxEvent::ParityError => interrupts_enabled.parity_err().bit_is_set(), - }; - if event_triggered { - events_triggered |= event; - } + if pending_interrupts.rxfifo_full().bit_is_set() { + active_events |= RxEvent::FifoFull; + } + if pending_interrupts.brk_det().bit_is_set() { + active_events |= RxEvent::BreakDetected; + } + if pending_interrupts.at_cmd_char_det().bit_is_set() { + active_events |= RxEvent::CmdCharDetected; + } + if pending_interrupts.rxfifo_ovf().bit_is_set() { + active_events |= RxEvent::FifoOvf; + } + if pending_interrupts.rxfifo_tout().bit_is_set() { + active_events |= RxEvent::FifoTout; + } + if pending_interrupts.glitch_det().bit_is_set() { + active_events |= RxEvent::GlitchDetected; + } + if pending_interrupts.frm_err().bit_is_set() { + active_events |= RxEvent::FrameError; + } + if pending_interrupts.parity_err().bit_is_set() { + active_events |= RxEvent::ParityError; } - events_triggered + + active_events } fn clear_rx_events(&self, events: impl Into>) { @@ -2335,50 +2649,60 @@ impl Info { /// Configures the RX-FIFO threshold /// - /// # Errors - /// [`Err(ConfigError::UnsupportedFifoThreshold)`][ConfigError::UnsupportedFifoThreshold] if provided value exceeds maximum value - /// for SOC : - /// - `esp32` **0x7F** - /// - `esp32c6`, `esp32h2` **0xFF** - /// - `esp32c3`, `esp32c2`, `esp32s2` **0x1FF** - /// - `esp32s3` **0x3FF** + /// ## Errors + /// + /// [ConfigError::UnsupportedRxFifoThreshold] if the provided value exceeds + /// [`Info::RX_FIFO_MAX_THRHD`]. fn set_rx_fifo_full_threshold(&self, threshold: u16) -> Result<(), ConfigError> { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - const MAX_THRHD: u16 = 0x7F; - } else if #[cfg(any(esp32c6, esp32h2))] { - const MAX_THRHD: u16 = 0xFF; - } else if #[cfg(any(esp32c3, esp32c2, esp32s2))] { - const MAX_THRHD: u16 = 0x1FF; - } else if #[cfg(esp32s3)] { - const MAX_THRHD: u16 = 0x3FF; - } + if threshold > Self::RX_FIFO_MAX_THRHD { + return Err(ConfigError::UnsupportedRxFifoThreshold); } - if threshold > MAX_THRHD { - return Err(ConfigError::UnsupportedFifoThreshold); + self.regs() + .conf1() + .modify(|_, w| unsafe { w.rxfifo_full_thrhd().bits(threshold as _) }); + + Ok(()) + } + + /// Reads the RX-FIFO threshold + #[allow(clippy::useless_conversion)] + fn rx_fifo_full_threshold(&self) -> u16 { + self.regs().conf1().read().rxfifo_full_thrhd().bits().into() + } + + /// Configures the TX-FIFO threshold + /// + /// ## Errors + /// + /// [ConfigError::UnsupportedTxFifoThreshold] if the provided value exceeds + /// [`Info::TX_FIFO_MAX_THRHD`]. + fn set_tx_fifo_empty_threshold(&self, threshold: u16) -> Result<(), ConfigError> { + if threshold > Self::TX_FIFO_MAX_THRHD { + return Err(ConfigError::UnsupportedTxFifoThreshold); } self.regs() .conf1() - .modify(|_, w| unsafe { w.rxfifo_full_thrhd().bits(threshold as _) }); + .modify(|_, w| unsafe { w.txfifo_empty_thrhd().bits(threshold as _) }); Ok(()) } /// Configures the Receive Timeout detection setting /// - /// # Arguments + /// ## Arguments + /// /// `timeout` - the number of symbols ("bytes") to wait for before /// triggering a timeout. Pass None to disable the timeout. /// - /// # Errors - /// [`Err(ConfigError::UnsupportedTimeout)`][ConfigError::UnsupportedTimeout] if the provided value exceeds - /// the maximum value for SOC : + /// ## Errors + /// + /// [ConfigError::UnsupportedTimeout] if the provided value exceeds + /// the maximum value for SOC: /// - `esp32`: Symbol size is fixed to 8, do not pass a value > **0x7F**. /// - `esp32c2`, `esp32c3`, `esp32c6`, `esp32h2`, esp32s2`, esp32s3`: The /// value you pass times the symbol size must be <= **0x3FF** - // TODO: the above should be a per-chip doc line. fn set_rx_timeout(&self, timeout: Option, _symbol_len: u8) -> Result<(), ConfigError> { cfg_if::cfg_if! { if #[cfg(esp32)] { @@ -2428,48 +2752,6 @@ impl Info { Ok(()) } - #[cfg(any(esp32c2, esp32c3, esp32s3))] - fn change_baud(&self, baudrate: u32, clock_source: ClockSource) { - use crate::peripherals::LPWR; - - let clocks = Clocks::get(); - let clk = match clock_source { - ClockSource::Apb => clocks.apb_clock.to_Hz(), - ClockSource::Xtal => clocks.xtal_clock.to_Hz(), - ClockSource::RcFast => crate::soc::constants::RC_FAST_CLK.to_Hz(), - }; - - if clock_source == ClockSource::RcFast { - LPWR::regs() - .clk_conf() - .modify(|_, w| w.dig_clk8m_en().variant(true)); - // esp_rom_delay_us(SOC_DELAY_RC_FAST_DIGI_SWITCH); - crate::rom::ets_delay_us(5); - } - - let max_div = 0b1111_1111_1111 - 1; - let clk_div = clk.div_ceil(max_div * baudrate); - self.regs().clk_conf().write(|w| unsafe { - w.sclk_sel().bits(match clock_source { - ClockSource::Apb => 1, - ClockSource::RcFast => 2, - ClockSource::Xtal => 3, - }); - w.sclk_div_a().bits(0); - w.sclk_div_b().bits(0); - w.sclk_div_num().bits(clk_div as u8 - 1); - w.rx_sclk_en().bit(true); - w.tx_sclk_en().bit(true) - }); - - let divider = (clk << 4) / (baudrate * clk_div); - let divider_integer = (divider >> 4) as u16; - let divider_frag = (divider & 0xf) as u8; - self.regs() - .clkdiv() - .write(|w| unsafe { w.clkdiv().bits(divider_integer).frag().bits(divider_frag) }); - } - fn is_instance(&self, other: impl Instance) -> bool { self == other.info() } @@ -2478,81 +2760,85 @@ impl Info { sync_regs(self.regs()); } - #[cfg(any(esp32c6, esp32h2))] - fn change_baud(&self, baudrate: u32, clock_source: ClockSource) { + fn change_baud(&self, config: &Config) -> Result<(), ConfigError> { let clocks = Clocks::get(); - let clk = match clock_source { - ClockSource::Apb => clocks.apb_clock.to_Hz(), - ClockSource::Xtal => clocks.xtal_clock.to_Hz(), - ClockSource::RcFast => crate::soc::constants::RC_FAST_CLK.to_Hz(), + let clk = match config.clock_source { + ClockSource::Apb => clocks.apb_clock.as_hz(), + #[cfg(not(any(esp32, esp32s2)))] + ClockSource::Xtal => clocks.xtal_clock.as_hz(), + #[cfg(not(any(esp32, esp32s2)))] + ClockSource::RcFast => crate::soc::constants::RC_FAST_CLK.as_hz(), + #[cfg(any(esp32, esp32s2))] + ClockSource::RefTick => crate::soc::constants::REF_TICK.as_hz(), }; - let max_div = 0b1111_1111_1111 - 1; - let clk_div = clk.div_ceil(max_div * baudrate); - - // UART clocks are configured via PCR - let pcr = crate::peripherals::PCR::regs(); + cfg_if::cfg_if! { + if #[cfg(any(esp32c2, esp32c3, esp32s3, esp32c6, esp32h2))] { + + const MAX_DIV: u32 = 0b1111_1111_1111 - 1; + let clk_div = (clk.div_ceil(MAX_DIV)).div_ceil(config.baudrate); + + // define `conf` in scope for modification below + cfg_if::cfg_if! { + if #[cfg(any(esp32c2, esp32c3, esp32s3))] { + if matches!(config.clock_source, ClockSource::RcFast) { + crate::peripherals::LPWR::regs() + .clk_conf() + .modify(|_, w| w.dig_clk8m_en().variant(true)); + // small delay whilst the clock source changes (SOC_DELAY_RC_FAST_DIGI_SWITCH from esp-idf) + crate::rom::ets_delay_us(5); + } + + let conf = self.regs().clk_conf(); + } else { + // UART clocks are configured via PCR + let pcr = crate::peripherals::PCR::regs(); + let conf = if self.is_instance(unsafe { crate::peripherals::UART0::steal() }) { + pcr.uart(0).clk_conf() + } else { + pcr.uart(1).clk_conf() + }; + } + }; - if self.is_instance(unsafe { crate::peripherals::UART0::steal() }) { - pcr.uart0_conf() - .modify(|_, w| w.uart0_rst_en().clear_bit().uart0_clk_en().set_bit()); - - pcr.uart0_sclk_conf().modify(|_, w| unsafe { - w.uart0_sclk_div_a().bits(0); - w.uart0_sclk_div_b().bits(0); - w.uart0_sclk_div_num().bits(clk_div as u8 - 1); - w.uart0_sclk_sel().bits(match clock_source { - ClockSource::Apb => 1, - ClockSource::RcFast => 2, - ClockSource::Xtal => 3, + conf.write(|w| unsafe { + w.sclk_sel().bits(match config.clock_source { + ClockSource::Apb => 1, + ClockSource::RcFast => 2, + ClockSource::Xtal => 3, + }); + w.sclk_div_a().bits(0); + w.sclk_div_b().bits(0); + w.sclk_div_num().bits(clk_div as u8 - 1) }); - w.uart0_sclk_en().set_bit() - }); - } else { - pcr.uart1_conf() - .modify(|_, w| w.uart1_rst_en().clear_bit().uart1_clk_en().set_bit()); - - pcr.uart1_sclk_conf().modify(|_, w| unsafe { - w.uart1_sclk_div_a().bits(0); - w.uart1_sclk_div_b().bits(0); - w.uart1_sclk_div_num().bits(clk_div as u8 - 1); - w.uart1_sclk_sel().bits(match clock_source { - ClockSource::Apb => 1, - ClockSource::RcFast => 2, - ClockSource::Xtal => 3, + + let divider = (clk << 4) / (config.baudrate * clk_div); + } else { + self.regs().conf0().modify(|_, w| { + w.tick_ref_always_on() + .bit(config.clock_source == ClockSource::Apb) }); - w.uart1_sclk_en().set_bit() - }); + + let divider = (clk << 4) / config.baudrate; + } } - let clk = clk / clk_div; - let divider = clk / baudrate; - let divider = divider as u16; + let divider_integer = divider >> 4; + let divider_frag = (divider & 0xf) as u8; - self.regs() - .clkdiv() - .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); + self.regs().clkdiv().write(|w| unsafe { + w.clkdiv() + .bits(divider_integer as _) + .frag() + .bits(divider_frag) + }); self.sync_regs(); - } - - #[cfg(any(esp32, esp32s2))] - fn change_baud(&self, baudrate: u32, clock_source: ClockSource) { - let clk = match clock_source { - ClockSource::Apb => Clocks::get().apb_clock.to_Hz(), - // ESP32(/-S2) TRM, section 3.2.4.2 (6.2.4.2 for S2) - ClockSource::RefTick => crate::soc::constants::REF_TICK.to_Hz(), - }; - - self.regs() - .conf0() - .modify(|_, w| w.tick_ref_always_on().bit(clock_source == ClockSource::Apb)); - let divider = clk / baudrate; + #[cfg(feature = "unstable")] + self.verify_baudrate(clk, config)?; - self.regs() - .clkdiv() - .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); + Ok(()) } fn change_data_bits(&self, data_bits: DataBits) { @@ -2587,7 +2873,7 @@ impl Info { #[cfg(not(esp32))] self.regs() .conf0() - .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) }); + .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8 + 1) }); } fn rxfifo_reset(&self) { @@ -2609,6 +2895,106 @@ impl Info { txfifo_rst(self.regs(), true); txfifo_rst(self.regs(), false); } + + #[cfg(feature = "unstable")] + fn verify_baudrate(&self, clk: u32, config: &Config) -> Result<(), ConfigError> { + // taken from https://github.com/espressif/esp-idf/blob/c5865270b50529cd32353f588d8a917d89f3dba4/components/hal/esp32c6/include/hal/uart_ll.h#L433-L444 + // (it's different for different chips) + let clkdiv_reg = self.regs().clkdiv().read(); + let clkdiv_frag = clkdiv_reg.frag().bits() as u32; + let clkdiv = clkdiv_reg.clkdiv().bits(); + + cfg_if::cfg_if! { + if #[cfg(any(esp32, esp32s2))] { + let actual_baud = (clk << 4) / ((clkdiv << 4) | clkdiv_frag); + } else if #[cfg(any(esp32c2, esp32c3, esp32s3))] { + let sclk_div_num = self.regs().clk_conf().read().sclk_div_num().bits() as u32; + let actual_baud = (clk << 4) / ((((clkdiv as u32) << 4) | clkdiv_frag) * (sclk_div_num + 1)); + } else { // esp32c6, esp32h2 + let pcr = crate::peripherals::PCR::regs(); + let conf = if self.is_instance(unsafe { crate::peripherals::UART0::steal() }) { + pcr.uart(0).clk_conf() + } else { + pcr.uart(1).clk_conf() + }; + let sclk_div_num = conf.read().sclk_div_num().bits() as u32; + let actual_baud = (clk << 4) / ((((clkdiv as u32) << 4) | clkdiv_frag) * (sclk_div_num + 1)); + } + }; + + match config.baudrate_tolerance { + BaudrateTolerance::Exact => { + let deviation = ((config.baudrate as i32 - actual_baud as i32).unsigned_abs() + * 100) + / actual_baud; + // We tolerate deviation of 1% from the desired baud value, as it never will be + // exactly the same + if deviation > 1_u32 { + return Err(ConfigError::UnachievableBaudrate); + } + } + BaudrateTolerance::ErrorPercent(percent) => { + let deviation = ((config.baudrate as i32 - actual_baud as i32).unsigned_abs() + * 100) + / actual_baud; + if deviation > percent as u32 { + return Err(ConfigError::UnachievableBaudrate); + } + } + _ => {} + } + + Ok(()) + } + + fn current_symbol_length(&self) -> u8 { + let conf0 = self.regs().conf0().read(); + let data_bits = conf0.bit_num().bits() + 5; // 5 data bits are encoded as variant 0 + let parity = conf0.parity_en().bit() as u8; + let mut stop_bits = conf0.stop_bit_num().bits(); + + match stop_bits { + 1 => { + // workaround for hardware issue, when UART stop bit set as 2-bit mode. + #[cfg(esp32)] + if self.regs().rs485_conf().read().dl1_en().bit_is_set() { + stop_bits = 2; + } + } + // esp-idf also counts 2 bits for settings 1.5 and 2 stop bits + _ => stop_bits = 2, + } + + 1 + data_bits + parity + stop_bits + } + + /// Reads one byte from the RX FIFO. + /// + /// If the FIFO is empty, the value of the returned byte is not specified. + fn read_next_from_fifo(&self) -> u8 { + fn access_fifo_register(f: impl Fn() -> R) -> R { + // https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/03-errata-description/esp32/cpu-subsequent-access-halted-when-get-interrupted.html + cfg_if::cfg_if! { + if #[cfg(esp32)] { + crate::interrupt::free(f) + } else { + f() + } + } + } + + let fifo_reg = self.regs().fifo(); + cfg_if::cfg_if! { + if #[cfg(esp32s2)] { + // On the ESP32-S2 we need to use PeriBus2 to read the FIFO: + let fifo_reg = unsafe { + &*fifo_reg.as_ptr().cast::().add(0x20C00000).cast::() + }; + } + } + + access_fifo_register(|| fifo_reg.read().rxfifo_rd_byte().bits()) + } } impl PartialEq for Info { @@ -2681,3 +3067,18 @@ impl Instance for AnyUart { } } } + +struct OnDrop(Option); +impl OnDrop { + fn new(cb: F) -> Self { + Self(Some(cb)) + } +} + +impl Drop for OnDrop { + fn drop(&mut self) { + if let Some(cb) = self.0.take() { + cb(); + } + } +} \ No newline at end of file From ed711b8ba2db886eaa4835f71f31270c8cdadb25 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Tue, 25 Feb 2025 15:09:47 -0500 Subject: [PATCH 28/34] fix: fmt --- esp-hal/src/uart.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index 411f251c27b..5d882d38c6b 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -1727,7 +1727,10 @@ fn rx_event_check_for_error(events: EnumSet) -> Result<(), RxError> { RxEvent::GlitchDetected => return Err(RxError::GlitchOccurred), RxEvent::FrameError => return Err(RxError::FrameFormatViolated), RxEvent::ParityError => return Err(RxError::ParityMismatch), - RxEvent::FifoFull | RxEvent::BreakDetected | RxEvent::CmdCharDetected | RxEvent::FifoTout => continue, + RxEvent::FifoFull + | RxEvent::BreakDetected + | RxEvent::CmdCharDetected + | RxEvent::FifoTout => continue, } } @@ -3081,4 +3084,4 @@ impl Drop for OnDrop { cb(); } } -} \ No newline at end of file +} From a05264ee52a68c728689d757e6cc3c09c65ee4ad Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Tue, 25 Feb 2025 15:10:52 -0500 Subject: [PATCH 29/34] fix: revert uart.rs --- esp-hal/src/uart.rs | 75 ++++++++++++--------------------------------- 1 file changed, 19 insertions(+), 56 deletions(-) diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index 5d882d38c6b..04371560ada 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -904,24 +904,6 @@ where Ok(to_read) } - /// Busy waits for a break condition to be detected on the RX - /// line. Condition is met when the receiver detects a NULL character - /// (i.e. logic 0 for one NULL character transmission) after stop bits. - /// - /// Clears the break detection interrupt before returning. - #[instability::unstable] - pub fn wait_for_break(&mut self) { - // Enable the break detection interrupt - self.regs().int_ena().write(|w| w.brk_det().bit(true)); - - while !self.regs().int_raw().read().brk_det().bit() { - // Just busy waiting - } - - // Clear the break detection interrupt - self.regs().int_clr().write(|w| w.brk_det().bit(true)); - } - #[allow(clippy::useless_conversion)] fn rx_fifo_count(&self) -> u16 { let fifo_cnt: u16 = self.regs().status().read().rxfifo_cnt().bits().into(); @@ -1119,11 +1101,6 @@ pub enum UartInterrupt { /// The transmitter has finished sending out all data from the FIFO. TxDone, - /// Break condition has been detected. - /// Triggered when the receiver detects a NULL character (i.e. logic 0 for - /// one NULL character transmission) after stop bits. - RxBreakDetected, - /// The receiver has received more data than what /// [`RxConfig::fifo_full_threshold`] specifies. RxFifoFull, @@ -1296,11 +1273,6 @@ where sync_regs(self.regs()); } - /// Busy waits for a break condition to be detected on the RX line. - pub fn wait_for_break(&mut self) { - self.rx.wait_for_break(); - } - /// Flush the transmit buffer of the UART pub fn flush(&mut self) -> Result<(), TxError> { self.tx.flush() @@ -1711,7 +1683,6 @@ pub(crate) enum TxEvent { #[derive(Debug, EnumSetType)] pub(crate) enum RxEvent { FifoFull, - BreakDetected, CmdCharDetected, FifoOvf, FifoTout, @@ -1727,10 +1698,7 @@ fn rx_event_check_for_error(events: EnumSet) -> Result<(), RxError> { RxEvent::GlitchDetected => return Err(RxError::GlitchOccurred), RxEvent::FrameError => return Err(RxError::FrameFormatViolated), RxEvent::ParityError => return Err(RxError::ParityMismatch), - RxEvent::FifoFull - | RxEvent::BreakDetected - | RxEvent::CmdCharDetected - | RxEvent::FifoTout => continue, + RxEvent::FifoFull | RxEvent::CmdCharDetected | RxEvent::FifoTout => continue, } } @@ -1867,11 +1835,23 @@ impl Uart<'_, Async> { self.rx.read_async(buf).await } - /// Asynchronously waits for a break condition on the RX line. - /// Condition is met when the receiver detects a NULL character (i.e. logic - /// 0 for one NULL character transmission) after stop bits. - pub async fn wait_for_break_async(&mut self) { - self.rx.wait_for_break_async().await; + /// Fill buffer asynchronously. + /// + /// This function reads data from the UART receive buffer into the + /// provided buffer. If the buffer is empty, the function waits + /// asynchronously for data to become available, or for an error to occur. + /// + /// Note that this function may ignore the `rx_fifo_full_threshold` setting + /// to ensure that it does not wait for more data than the buffer can hold. + /// + /// ## Cancellation + /// + /// This function is **not** cancellation safe. If the future is dropped + /// before it resolves, or if an error occurs during the read operation, + /// previously read data may be lost. + #[instability::unstable] + pub async fn read_exact_async(&mut self, buf: &mut [u8]) -> Result<(), RxError> { + self.rx.read_exact_async(buf).await } /// Write data into the TX buffer. @@ -1991,7 +1971,6 @@ impl UartRx<'_, Async> { // Wait for space or event let mut events = RxEvent::FifoFull - | RxEvent::BreakDetected | RxEvent::FifoOvf | RxEvent::FrameError | RxEvent::GlitchDetected @@ -2075,13 +2054,6 @@ impl UartRx<'_, Async> { Ok(()) } - - /// Interrupt-driven wait for a break condition on the RX line. - /// Condition is met when the receiver detects a NULL character (i.e. logic - /// 0 for one NULL character transmission) after stop bits. - pub async fn wait_for_break_async(&mut self) { - UartRxFuture::new(self.uart.reborrow(), RxEvent::BreakDetected).await; - } } #[instability::unstable] @@ -2472,7 +2444,6 @@ impl Info { UartInterrupt::AtCmd => w.at_cmd_char_det().bit(enable), UartInterrupt::TxDone => w.tx_done().bit(enable), UartInterrupt::RxFifoFull => w.rxfifo_full().bit(enable), - UartInterrupt::BreakDetected => w.brk_det().bit(enable), }; } w @@ -2494,9 +2465,6 @@ impl Info { if ints.rxfifo_full().bit_is_set() { res.insert(UartInterrupt::RxFifoFull); } - if ints.brk_det().bit_is_set() { - res.insert(UartInterrupt::BreakDetected); - } res } @@ -2510,7 +2478,6 @@ impl Info { UartInterrupt::AtCmd => w.at_cmd_char_det().clear_bit_by_one(), UartInterrupt::TxDone => w.tx_done().clear_bit_by_one(), UartInterrupt::RxFifoFull => w.rxfifo_full().clear_bit_by_one(), - UartInterrupt::BreakDetected => w.brk_det().clear_bit_by_one(), }; } w @@ -2605,9 +2572,6 @@ impl Info { if pending_interrupts.rxfifo_full().bit_is_set() { active_events |= RxEvent::FifoFull; } - if pending_interrupts.brk_det().bit_is_set() { - active_events |= RxEvent::BreakDetected; - } if pending_interrupts.at_cmd_char_det().bit_is_set() { active_events |= RxEvent::CmdCharDetected; } @@ -2636,7 +2600,6 @@ impl Info { for event in events { match event { RxEvent::FifoFull => w.rxfifo_full().clear_bit_by_one(), - RxEvent::BreakDetected => w.brk_det().clear_bit_by_one(), RxEvent::CmdCharDetected => w.at_cmd_char_det().clear_bit_by_one(), RxEvent::FifoOvf => w.rxfifo_ovf().clear_bit_by_one(), @@ -3084,4 +3047,4 @@ impl Drop for OnDrop { cb(); } } -} +} \ No newline at end of file From aa29c3a54204f7ede71d377f961cc49e27bdaf67 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Tue, 25 Feb 2025 15:11:26 -0500 Subject: [PATCH 30/34] fix: fmt --- esp-hal/src/uart.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index 04371560ada..ff56451ee97 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -3047,4 +3047,4 @@ impl Drop for OnDrop { cb(); } } -} \ No newline at end of file +} From 351ea0ecdc66fe5f39e2862146a3c6b18d21c696 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Tue, 25 Feb 2025 15:18:27 -0500 Subject: [PATCH 31/34] fix: repush to match with main --- esp-hal/src/uart.rs | 74 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 18 deletions(-) diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index ff56451ee97..129a41684b4 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -904,6 +904,24 @@ where Ok(to_read) } + /// Busy waits for a break condition to be detected on the RX + /// line. Condition is met when the receiver detects a NULL character + /// (i.e. logic 0 for one NULL character transmission) after stop bits. + /// + /// Clears the break detection interrupt before returning. + #[instability::unstable] + pub fn wait_for_break(&mut self) { + // Enable the break detection interrupt + self.regs().int_ena().write(|w| w.brk_det().bit(true)); + + while !self.regs().int_raw().read().brk_det().bit() { + // Just busy waiting + } + + // Clear the break detection interrupt + self.regs().int_clr().write(|w| w.brk_det().bit(true)); + } + #[allow(clippy::useless_conversion)] fn rx_fifo_count(&self) -> u16 { let fifo_cnt: u16 = self.regs().status().read().rxfifo_cnt().bits().into(); @@ -1101,6 +1119,11 @@ pub enum UartInterrupt { /// The transmitter has finished sending out all data from the FIFO. TxDone, + /// Break condition has been detected. + /// Triggered when the receiver detects a NULL character (i.e. logic 0 for + /// one NULL character transmission) after stop bits. + RxBreakDetected, + /// The receiver has received more data than what /// [`RxConfig::fifo_full_threshold`] specifies. RxFifoFull, @@ -1273,6 +1296,11 @@ where sync_regs(self.regs()); } + /// Busy waits for a break condition to be detected on the RX line. + pub fn wait_for_break(&mut self) { + self.rx.wait_for_break(); + } + /// Flush the transmit buffer of the UART pub fn flush(&mut self) -> Result<(), TxError> { self.tx.flush() @@ -1683,6 +1711,7 @@ pub(crate) enum TxEvent { #[derive(Debug, EnumSetType)] pub(crate) enum RxEvent { FifoFull, + BreakDetected, CmdCharDetected, FifoOvf, FifoTout, @@ -1698,7 +1727,10 @@ fn rx_event_check_for_error(events: EnumSet) -> Result<(), RxError> { RxEvent::GlitchDetected => return Err(RxError::GlitchOccurred), RxEvent::FrameError => return Err(RxError::FrameFormatViolated), RxEvent::ParityError => return Err(RxError::ParityMismatch), - RxEvent::FifoFull | RxEvent::CmdCharDetected | RxEvent::FifoTout => continue, + RxEvent::FifoFull + | RxEvent::BreakDetected + | RxEvent::CmdCharDetected + | RxEvent::FifoTout => continue, } } @@ -1835,23 +1867,11 @@ impl Uart<'_, Async> { self.rx.read_async(buf).await } - /// Fill buffer asynchronously. - /// - /// This function reads data from the UART receive buffer into the - /// provided buffer. If the buffer is empty, the function waits - /// asynchronously for data to become available, or for an error to occur. - /// - /// Note that this function may ignore the `rx_fifo_full_threshold` setting - /// to ensure that it does not wait for more data than the buffer can hold. - /// - /// ## Cancellation - /// - /// This function is **not** cancellation safe. If the future is dropped - /// before it resolves, or if an error occurs during the read operation, - /// previously read data may be lost. - #[instability::unstable] - pub async fn read_exact_async(&mut self, buf: &mut [u8]) -> Result<(), RxError> { - self.rx.read_exact_async(buf).await + /// Asynchronously waits for a break condition on the RX line. + /// Condition is met when the receiver detects a NULL character (i.e. logic + /// 0 for one NULL character transmission) after stop bits. + pub async fn wait_for_break_async(&mut self) { + self.rx.wait_for_break_async().await; } /// Write data into the TX buffer. @@ -1971,6 +1991,7 @@ impl UartRx<'_, Async> { // Wait for space or event let mut events = RxEvent::FifoFull + | RxEvent::BreakDetected | RxEvent::FifoOvf | RxEvent::FrameError | RxEvent::GlitchDetected @@ -2054,6 +2075,13 @@ impl UartRx<'_, Async> { Ok(()) } + + /// Interrupt-driven wait for a break condition on the RX line. + /// Condition is met when the receiver detects a NULL character (i.e. logic + /// 0 for one NULL character transmission) after stop bits. + pub async fn wait_for_break_async(&mut self) { + UartRxFuture::new(self.uart.reborrow(), RxEvent::BreakDetected).await; + } } #[instability::unstable] @@ -2444,6 +2472,7 @@ impl Info { UartInterrupt::AtCmd => w.at_cmd_char_det().bit(enable), UartInterrupt::TxDone => w.tx_done().bit(enable), UartInterrupt::RxFifoFull => w.rxfifo_full().bit(enable), + UartInterrupt::RxBreakDetected => w.brk_det().bit(enable), }; } w @@ -2465,6 +2494,9 @@ impl Info { if ints.rxfifo_full().bit_is_set() { res.insert(UartInterrupt::RxFifoFull); } + if ints.brk_det().bit_is_set() { + res.insert(UartInterrupt::RxBreakDetected); + } res } @@ -2478,6 +2510,7 @@ impl Info { UartInterrupt::AtCmd => w.at_cmd_char_det().clear_bit_by_one(), UartInterrupt::TxDone => w.tx_done().clear_bit_by_one(), UartInterrupt::RxFifoFull => w.rxfifo_full().clear_bit_by_one(), + UartInterrupt::RxBreakDetected => w.brk_det().clear_bit_by_one(), }; } w @@ -2552,6 +2585,7 @@ impl Info { for event in events { match event { RxEvent::FifoFull => w.rxfifo_full().bit(enable), + RxEvent::BreakDetected => w.brk_det().bit(enable), RxEvent::CmdCharDetected => w.at_cmd_char_det().bit(enable), RxEvent::FifoOvf => w.rxfifo_ovf().bit(enable), @@ -2572,6 +2606,9 @@ impl Info { if pending_interrupts.rxfifo_full().bit_is_set() { active_events |= RxEvent::FifoFull; } + if pending_interrupts.brk_det().bit_is_set() { + active_events |= RxEvent::BreakDetected; + } if pending_interrupts.at_cmd_char_det().bit_is_set() { active_events |= RxEvent::CmdCharDetected; } @@ -2600,6 +2637,7 @@ impl Info { for event in events { match event { RxEvent::FifoFull => w.rxfifo_full().clear_bit_by_one(), + RxEvent::BreakDetected => w.brk_det().clear_bit_by_one(), RxEvent::CmdCharDetected => w.at_cmd_char_det().clear_bit_by_one(), RxEvent::FifoOvf => w.rxfifo_ovf().clear_bit_by_one(), From 8d01053edca6073844c5bf7f2912a1c575b9a5c5 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Tue, 25 Feb 2025 15:41:19 -0500 Subject: [PATCH 32/34] fix: `.rx` --- esp-hal/src/uart.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index 129a41684b4..3191ad1a13b 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -2091,7 +2091,7 @@ impl embedded_io_async::Read for Uart<'_, Async> { } async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ReadExactError> { - self.read_exact_async(buf) + self.rx.read_exact_async(buf) .await .map_err(|e| ReadExactError::Other(IoError::Rx(e))) } From bd3d1aa43df4845c418851ad847a2da5f80dcd60 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Tue, 25 Feb 2025 16:29:24 -0500 Subject: [PATCH 33/34] fix: fmt --- esp-hal/src/uart.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index 3191ad1a13b..854fc940675 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -2091,7 +2091,8 @@ impl embedded_io_async::Read for Uart<'_, Async> { } async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ReadExactError> { - self.rx.read_exact_async(buf) + self.rx + .read_exact_async(buf) .await .map_err(|e| ReadExactError::Other(IoError::Rx(e))) } From a710fcc5fe1b7d25ff102c915d33d9a77c25d1e3 Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Tue, 25 Feb 2025 16:38:50 -0500 Subject: [PATCH 34/34] chore: cleanup examples --- examples/src/bin/uart_break_detection.rs | 10 ++++------ examples/src/bin/uart_break_detection_async.rs | 7 ++----- examples/src/bin/uart_interrupts.rs | 7 +++---- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/examples/src/bin/uart_break_detection.rs b/examples/src/bin/uart_break_detection.rs index 1203c3a95fd..5ff38757b84 100644 --- a/examples/src/bin/uart_break_detection.rs +++ b/examples/src/bin/uart_break_detection.rs @@ -1,10 +1,10 @@ //! Blocking UART break detection example. //! //! The following wiring is assumed: -//! - TX => GPIO17 //! - RX => GPIO16 //% CHIPS: esp32 +//% FEATURES: esp-hal/unstable #![no_std] #![no_main] @@ -12,7 +12,7 @@ use esp_backtrace as _; use esp_hal::{ main, - uart::{Config as UartConfig, DataBits, Parity, RxConfig, StopBits, Uart}, + uart::{Config as UartConfig, DataBits, Parity, StopBits, Uart}, }; #[main] @@ -22,12 +22,10 @@ fn main() -> ! { .with_baudrate(19200) .with_data_bits(DataBits::_8) .with_parity(Parity::None) - .with_stop_bits(StopBits::_1) - .with_rx(RxConfig::default().with_fifo_full_threshold(1)); + .with_stop_bits(StopBits::_1); let mut uart = Uart::new(peripherals.UART1, uart_config) .expect("Failed to initialize UART") - .with_rx(peripherals.GPIO16) - .with_tx(peripherals.GPIO17); + .with_rx(peripherals.GPIO16); loop { uart.wait_for_break(); diff --git a/examples/src/bin/uart_break_detection_async.rs b/examples/src/bin/uart_break_detection_async.rs index 7b1fcfee2ce..09305b899ab 100644 --- a/examples/src/bin/uart_break_detection_async.rs +++ b/examples/src/bin/uart_break_detection_async.rs @@ -1,7 +1,6 @@ //! Async UART break detection example. //! //! The following wiring is assumed: -//! - TX => GPIO17 //! - RX => GPIO16 //% CHIPS: esp32 @@ -12,7 +11,7 @@ use embassy_executor::Spawner; use esp_backtrace as _; -use esp_hal::uart::{Config as UartConfig, DataBits, Parity, RxConfig, StopBits, Uart}; +use esp_hal::uart::{Config as UartConfig, DataBits, Parity, StopBits, Uart}; #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { @@ -21,12 +20,10 @@ async fn main(_spawner: Spawner) { .with_baudrate(19200) .with_data_bits(DataBits::_8) .with_parity(Parity::None) - .with_stop_bits(StopBits::_1) - .with_rx(RxConfig::default().with_fifo_full_threshold(1)); + .with_stop_bits(StopBits::_1); let mut uart = Uart::new(peripherals.UART1, uart_config) .expect("Failed to initialize UART") .with_rx(peripherals.GPIO16) - .with_tx(peripherals.GPIO17) .into_async(); loop { diff --git a/examples/src/bin/uart_interrupts.rs b/examples/src/bin/uart_interrupts.rs index c0da47ab4d5..c6591a28ae6 100644 --- a/examples/src/bin/uart_interrupts.rs +++ b/examples/src/bin/uart_interrupts.rs @@ -1,10 +1,10 @@ //! Example of responding to UART interrupts. //! //! The following wiring is assumed: -//! - TX => GPIO17 //! - RX => GPIO16 //% CHIPS: esp32 +//% FEATURES: esp-hal/unstable #![no_std] #![no_main] @@ -34,8 +34,7 @@ fn main() -> ! { .with_rx(RxConfig::default().with_fifo_full_threshold(1)); let mut uart = Uart::new(peripherals.UART1, uart_config) .expect("Failed to initialize UART") - .with_rx(peripherals.GPIO16) - .with_tx(peripherals.GPIO17); + .with_rx(peripherals.GPIO16); uart.set_interrupt_handler(handler); @@ -60,7 +59,7 @@ fn handler() { } if serial.interrupts().contains(UartInterrupt::RxFifoFull) { let mut byte = [0u8; 1]; - serial.read_bytes(&mut byte).unwrap(); + serial.read(&mut byte).unwrap(); esp_println::print!(" {:02X}", byte[0]); }