From 3d147249825d2e1b9fc8a595b499fcc24f1b8d6d Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Sat, 21 Dec 2024 11:31:32 -0500 Subject: [PATCH 01/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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},