Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `DmaTxBuffer` and `DmaRxBuffer` now have a `Final` associated type. (#3923)
- `RsaBackend, RsaContext`: Work-queue based RSA driver (#3910)
- `aes::{AesBackend, AesContext, dma::AesDmaBackend}`: Work-queue based AES driver (#3880, #3897)
- Added `send_break` for sending software breaks with the UART driver (#4284)
- `aes::cipher_modes`, `aes::CipherState` for constructing `AesContext`s (#3895)
- `aes::dma::DmaCipherState` so that `AesDma` can properly support cipher modes that require state (IV, nonce, etc.) (#3897)
- `uart::Uhci`: for UART with DMA using the UHCI peripheral (#3871, #4008, #4011)
Expand Down
157 changes: 156 additions & 1 deletion esp-hal/src/uart/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ where
guard: tx_guard,
rts_pin,
tx_pin,
baudrate: config.baudrate,
},
};
serial.init(config)?;
Expand Down Expand Up @@ -518,6 +519,7 @@ pub struct UartTx<'d, Dm: DriverMode> {
guard: PeripheralGuard,
rts_pin: PinGuard,
tx_pin: PinGuard,
baudrate: u32,
}

/// UART (Receive)
Expand Down Expand Up @@ -617,6 +619,7 @@ where
type ConfigError = ConfigError;

fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
self.baudrate = config.baudrate;
self.apply_config(config)
}
}
Expand Down Expand Up @@ -660,6 +663,7 @@ impl<'d> UartTx<'d, Blocking> {
guard: self.guard,
rts_pin: self.rts_pin,
tx_pin: self.tx_pin,
baudrate: self.baudrate,
}
}
}
Expand All @@ -682,6 +686,7 @@ impl<'d> UartTx<'d, Async> {
guard: self.guard,
rts_pin: self.rts_pin,
tx_pin: self.tx_pin,
baudrate: self.baudrate,
}
}

Expand Down Expand Up @@ -855,6 +860,37 @@ where
while !self.is_tx_idle() {}
}

/// Sends a break signal for a specified duration in bit time.
///
/// Duration is in bits, the time it takes to transfer one bit at the
/// current baud rate. The delay during the break is just is busy-waiting.
#[instability::unstable]
pub fn send_break(&mut self, bits: u32) {
// Read the current TX inversion state
let original_txd_inv = self.uart.info().regs().conf0().read().txd_inv().bit();

// Invert the TX line (toggle the current state)
self.uart
.info()
.regs()
.conf0()
.modify(|_, w| w.txd_inv().bit(!original_txd_inv));

// Calculate total delay in microseconds: (bits * 1_000_000) / baudrate_bps
// Use u64 to avoid overflow, then convert back to u32
let total_delay_us = (bits as u64 * 1_000_000) / self.baudrate as u64;
let delay_us = (total_delay_us as u32).max(1);

crate::rom::ets_delay_us(delay_us);

// Restore the original TX inversion state
self.uart
.info()
.regs()
.conf0()
.modify(|_, w| w.txd_inv().bit(original_txd_inv));
}

/// Checks if the TX line is idle for this UART instance.
///
/// Returns `true` if the transmit line is idle, meaning no data is
Expand Down Expand Up @@ -936,6 +972,46 @@ impl<'d> UartRx<'d, Blocking> {
Ok(uart_rx)
}

/// Waits for a break condition to be detected.
///
/// This is a blocking function that will continuously check for a break condition.
/// After detection, the break interrupt flag is automatically cleared.
#[instability::unstable]
pub fn wait_for_break(&mut self) {
while !self.regs().int_raw().read().brk_det().bit_is_set() {
// wait
}
self.regs()
.int_clr()
.write(|w| w.brk_det().clear_bit_by_one());
}

/// Waits for a break condition to be detected with a timeout.
///
/// This is a blocking function that will check for a break condition up to
/// the specified timeout. Returns `true` if a break was detected, `false` if
/// the timeout elapsed. After successful detection, the break interrupt flag
/// is automatically cleared.
///
/// ## Arguments
/// * `timeout_us` - Timeout in microseconds
#[instability::unstable]
pub fn wait_for_break_with_timeout(&mut self, timeout_us: u32) -> bool {
let start = crate::time::Instant::now();
let timeout_duration = crate::time::Duration::from_micros(timeout_us as u64);

while !self.regs().int_raw().read().brk_det().bit_is_set() {
if crate::time::Instant::now() - start >= timeout_duration {
return false;
}
}

self.regs()
.int_clr()
.write(|w| w.brk_det().clear_bit_by_one());
true
}

/// Reconfigures the driver to operate in [`Async`] mode.
#[instability::unstable]
pub fn into_async(self) -> UartRx<'d, Async> {
Expand Down Expand Up @@ -1089,6 +1165,23 @@ impl<'d> UartRx<'d, Async> {

Ok(())
}

/// Waits for a break condition to be detected asynchronously.
///
/// This is an async function that will await until a break condition is
/// detected on the RX line. After detection, the break interrupt flag is
/// automatically cleared.
///
/// ## Cancellation
///
/// This function is cancellation safe.
#[instability::unstable]
pub async fn wait_for_break_async(&mut self) {
UartRxFuture::new(self.uart.reborrow(), RxEvent::BreakDetected).await;
self.regs()
.int_clr()
.write(|w| w.brk_det().clear_bit_by_one());
}
}

impl<'d, Dm> UartRx<'d, Dm>
Expand Down Expand Up @@ -1379,6 +1472,29 @@ impl<'d> Uart<'d, Blocking> {
pub fn clear_interrupts(&mut self, interrupts: EnumSet<UartInterrupt>) {
self.tx.uart.info().clear_interrupts(interrupts)
}

/// Waits for a break condition to be detected.
///
/// This is a blocking function that will continuously check for a break condition.
/// After detection, the break interrupt flag is automatically cleared.
#[instability::unstable]
pub fn wait_for_break(&mut self) {
self.rx.wait_for_break()
}

/// Waits for a break condition to be detected with a timeout.
///
/// This is a blocking function that will check for a break condition up to
/// the specified timeout. Returns `true` if a break was detected, `false` if
/// the timeout elapsed. After successful detection, the break interrupt flag
/// is automatically cleared.
///
/// ## Arguments
/// * `timeout_us` - Timeout in microseconds
#[instability::unstable]
pub fn wait_for_break_with_timeout(&mut self, timeout_us: u32) -> bool {
self.rx.wait_for_break_with_timeout(timeout_us)
}
}

impl<'d> Uart<'d, Async> {
Expand Down Expand Up @@ -1518,6 +1634,20 @@ impl<'d> Uart<'d, Async> {
pub async fn read_exact_async(&mut self, buf: &mut [u8]) -> Result<(), RxError> {
self.rx.read_exact_async(buf).await
}

/// Waits for a break condition to be detected asynchronously.
///
/// This is an async function that will await until a break condition is
/// detected on the RX line. After detection, the break interrupt flag is
/// automatically cleared.
///
/// ## Cancellation
///
/// This function is cancellation safe.
#[instability::unstable]
pub async fn wait_for_break_async(&mut self) {
self.rx.wait_for_break_async().await
}
}

/// List of exposed UART events.
Expand All @@ -1533,6 +1663,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,
Expand Down Expand Up @@ -1690,6 +1825,12 @@ where
self.tx.flush()
}

/// Sends a break signal for a specified duration
#[instability::unstable]
pub fn send_break(&mut self, bits: u32) {
self.tx.send_break(bits)
}

/// Returns whether the UART buffer has data.
///
/// If this function returns `true`, [`Self::read`] will not block.
Expand Down Expand Up @@ -2271,6 +2412,7 @@ pub(crate) enum RxEvent {
GlitchDetected,
FrameError,
ParityError,
BreakDetected,
}

fn rx_event_check_for_error(events: EnumSet<RxEvent>) -> Result<(), RxError> {
Expand All @@ -2280,7 +2422,10 @@ fn rx_event_check_for_error(events: EnumSet<RxEvent>) -> Result<(), RxError> {
RxEvent::GlitchDetected => return Err(RxError::GlitchOccurred),
RxEvent::FrameError => return Err(RxError::FrameFormatViolated),
RxEvent::ParityError => return Err(RxError::ParityMismatch),
RxEvent::FifoFull | RxEvent::CmdCharDetected | RxEvent::FifoTout => continue,
RxEvent::FifoFull
| RxEvent::CmdCharDetected
| RxEvent::FifoTout
| RxEvent::BreakDetected => continue,
}
}

Expand Down Expand Up @@ -2838,6 +2983,7 @@ impl Info {
match interrupt {
UartInterrupt::AtCmd => w.at_cmd_char_det().bit(enable),
UartInterrupt::TxDone => w.tx_done().bit(enable),
UartInterrupt::RxBreakDetected => w.brk_det().bit(enable),
UartInterrupt::RxFifoFull => w.rxfifo_full().bit(enable),
UartInterrupt::RxTimeout => w.rxfifo_tout().bit(enable),
};
Expand All @@ -2858,6 +3004,9 @@ impl Info {
if ints.tx_done().bit_is_set() {
res.insert(UartInterrupt::TxDone);
}
if ints.brk_det().bit_is_set() {
res.insert(UartInterrupt::RxBreakDetected);
}
if ints.rxfifo_full().bit_is_set() {
res.insert(UartInterrupt::RxFifoFull);
}
Expand All @@ -2876,6 +3025,7 @@ impl Info {
match interrupt {
UartInterrupt::AtCmd => w.at_cmd_char_det().clear_bit_by_one(),
UartInterrupt::TxDone => w.tx_done().clear_bit_by_one(),
UartInterrupt::RxBreakDetected => w.brk_det().clear_bit_by_one(),
UartInterrupt::RxFifoFull => w.rxfifo_full().clear_bit_by_one(),
UartInterrupt::RxTimeout => w.rxfifo_tout().clear_bit_by_one(),
};
Expand Down Expand Up @@ -2939,6 +3089,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),
Expand All @@ -2959,6 +3110,9 @@ impl Info {
if pending_interrupts.rxfifo_full().bit_is_set() {
active_events |= RxEvent::FifoFull;
}
if pending_interrupts.brk_det().bit_is_set() {
active_events |= RxEvent::BreakDetected;
}
if pending_interrupts.at_cmd_char_det().bit_is_set() {
active_events |= RxEvent::CmdCharDetected;
}
Expand Down Expand Up @@ -2987,6 +3141,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(),
Expand Down
24 changes: 24 additions & 0 deletions examples/interrupt/uart/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[target.'cfg(target_arch = "riscv32")']
runner = "espflash flash --monitor"
rustflags = [
"-C", "link-arg=-Tlinkall.x",
"-C", "force-frame-pointers",
]

[target.'cfg(target_arch = "xtensa")']
runner = "espflash flash --monitor"
rustflags = [
# GNU LD
"-C", "link-arg=-Wl,-Tlinkall.x",
"-C", "link-arg=-nostartfiles",

# LLD
# "-C", "link-arg=-Tlinkall.x",
# "-C", "linker=rust-lld",
]

[env]
ESP_LOG = "info"

[unstable]
build-std = ["core", "alloc"]
55 changes: 55 additions & 0 deletions examples/interrupt/uart/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
[package]
name = "uart_interrupt"
version = "0.0.0"
edition = "2024"
publish = false

[dependencies]
cfg-if = "1.0.0"
critical-section = "1.1.3"
esp-backtrace = { path = "../../../esp-backtrace", features = [
"panic-handler",
"println",
] }
esp-bootloader-esp-idf = { path = "../../../esp-bootloader-esp-idf" }
esp-hal = { path = "../../../esp-hal", features = ["log-04", "unstable"] }
esp-println = { path = "../../../esp-println", features = ["log-04"] }

[features]
esp32 = ["esp-backtrace/esp32", "esp-bootloader-esp-idf/esp32", "esp-hal/esp32"]
esp32c2 = [
"esp-backtrace/esp32c2",
"esp-bootloader-esp-idf/esp32c2",
"esp-hal/esp32c2",
]
esp32c3 = [
"esp-backtrace/esp32c3",
"esp-bootloader-esp-idf/esp32c3",
"esp-hal/esp32c3",
]
esp32c6 = [
"esp-backtrace/esp32c6",
"esp-bootloader-esp-idf/esp32c6",
"esp-hal/esp32c6",
]
esp32h2 = [
"esp-backtrace/esp32h2",
"esp-bootloader-esp-idf/esp32h2",
"esp-hal/esp32h2",
]
esp32s2 = [
"esp-backtrace/esp32s2",
"esp-bootloader-esp-idf/esp32s2",
"esp-hal/esp32s2",
]
esp32s3 = [
"esp-backtrace/esp32s3",
"esp-bootloader-esp-idf/esp32s3",
"esp-hal/esp32s3",
]

[profile.release]
debug = true
debug-assertions = true
lto = "fat"
codegen-units = 1
Loading
Loading