Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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 @@ -18,6 +18,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
73 changes: 72 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 @@
guard: tx_guard,
rts_pin,
tx_pin,
baudrate: config.baudrate,
},
};
serial.init(config)?;
Expand Down Expand Up @@ -518,6 +519,7 @@
guard: PeripheralGuard,
rts_pin: PinGuard,
tx_pin: PinGuard,
baudrate: u32,
}

/// UART (Receive)
Expand Down Expand Up @@ -617,6 +619,7 @@
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 @@
guard: self.guard,
rts_pin: self.rts_pin,
tx_pin: self.tx_pin,
baudrate: self.baudrate,
}
}
}
Expand All @@ -682,6 +686,7 @@
guard: self.guard,
rts_pin: self.rts_pin,
tx_pin: self.tx_pin,
baudrate: self.baudrate,
}
}

Expand Down Expand Up @@ -855,6 +860,37 @@
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,16 @@
Ok(uart_rx)
}

/// Waits for a break condition to be detected.
///
/// This is a blocking function that will continuously check for a break condition.
#[instability::unstable]
pub fn wait_for_break(&mut self) {
while !self.uart.info().brk_det().bit_is_set() {

Check failure on line 980 in esp-hal/src/uart/mod.rs

View workflow job for this annotation

GitHub Actions / esp-hal

no method named `brk_det` found for reference `&'static uart::Info` in the current scope

Check failure on line 980 in esp-hal/src/uart/mod.rs

View workflow job for this annotation

GitHub Actions / esp-hal

no method named `brk_det` found for reference `&'static uart::Info` in the current scope

Check failure on line 980 in esp-hal/src/uart/mod.rs

View workflow job for this annotation

GitHub Actions / esp-hal

no method named `brk_det` found for reference `&'static uart::Info` in the current scope
// wait
}
}

Comment on lines 975 to 1014
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bugadani - I added wait_for_break to complete the HIL test but I would intend to use it more as an interrupt handler now that we have Rx event for break detection.

Do you approve of this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think doing both should be fine. This function has lower latency than any interrupt handler would, so it can probably stay and remain useful in this form.

It makes sense to add an UartInterrupt::Break variant.

It would be nice to have a wait_for_break_with_timeout variant, and an async fn wait_for_break_async, too.

/// Reconfigures the driver to operate in [`Async`] mode.
#[instability::unstable]
pub fn into_async(self) -> UartRx<'d, Async> {
Expand Down Expand Up @@ -1533,6 +1579,11 @@
/// 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 +1741,12 @@
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 +2328,7 @@
GlitchDetected,
FrameError,
ParityError,
BreakDetected,
}

fn rx_event_check_for_error(events: EnumSet<RxEvent>) -> Result<(), RxError> {
Expand All @@ -2280,7 +2338,10 @@
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 +2899,7 @@
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 +2920,9 @@
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 +2941,7 @@
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 +3005,7 @@
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 +3026,9 @@
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 +3057,7 @@
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"]
54 changes: 54 additions & 0 deletions examples/interrupt/uart/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
[package]
name = "uart_interrupt"
version = "0.0.0"
edition = "2024"
publish = false

[dependencies]
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
65 changes: 65 additions & 0 deletions examples/interrupt/uart/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//! Example of responding to UART interrupts.
//!
//! The following wiring is assumed:
//! - RX => GPIO16

#![no_std]
#![no_main]

use core::cell::RefCell;

use critical_section::Mutex;
use esp_backtrace as _;
use esp_hal::{
Blocking,
handler,
main,
ram,
uart::{Config as UartConfig, DataBits, Parity, RxConfig, StopBits, Uart, UartInterrupt},
};

static SERIAL: Mutex<RefCell<Option<Uart<Blocking>>>> = Mutex::new(RefCell::new(None));

#[main]
fn main() -> ! {
let peripherals = esp_hal::init(esp_hal::Config::default());
let uart_config = UartConfig::default()
.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);

uart.set_interrupt_handler(handler);

critical_section::with(|cs| {
uart.clear_interrupts(UartInterrupt::RxBreakDetected | UartInterrupt::RxFifoFull);
uart.listen(UartInterrupt::RxBreakDetected | UartInterrupt::RxFifoFull);
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::RxBreakDetected) {
esp_println::print!("\nBREAK");
}
if serial.interrupts().contains(UartInterrupt::RxFifoFull) {
let mut byte = [0u8; 1];
serial.read(&mut byte).unwrap();
esp_println::print!(" {:02X}", byte[0]);
}

serial.clear_interrupts(UartInterrupt::RxBreakDetected | UartInterrupt::RxFifoFull);
});
}
24 changes: 24 additions & 0 deletions examples/peripheral/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"]
Loading
Loading