Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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 (#3177)
- `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
61 changes: 60 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,35 @@ where
while !self.is_tx_idle() {}
}

/// Sends a break signal for a specified duration in bit time, i.e. the time
/// it takes to transfer one bit at the current baud rate. The delay during
/// the break is just is busy-waiting.
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 @@ -1533,6 +1567,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 +1729,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 +2316,7 @@ pub(crate) enum RxEvent {
GlitchDetected,
FrameError,
ParityError,
BreakDetected,
}

fn rx_event_check_for_error(events: EnumSet<RxEvent>) -> Result<(), RxError> {
Expand All @@ -2280,7 +2326,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 +2887,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 +2908,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 +2929,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 +2993,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 +3014,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 +3045,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"]
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
68 changes: 68 additions & 0 deletions examples/interrupt/uart/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//! Example of responding to UART interrupts.
//!
//! The following wiring is assumed:
//! - RX => GPIO16

//% CHIPS: esp32
//% FEATURES: esp-hal/unstable

#![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