-
Notifications
You must be signed in to change notification settings - Fork 344
feat: uart break send + detect #4284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 9 commits
9be5d23
91ef610
97bb924
8426ce4
ef52491
56fd47e
b49bd13
0001ee1
d8b7544
e750ea7
ebda145
70861fb
5ff884b
dcbd3b4
fd088aa
adc4fa5
d54743a
1a8a819
4a5dfa4
2b83646
fa05bbd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -482,6 +482,7 @@ | |
guard: tx_guard, | ||
rts_pin, | ||
tx_pin, | ||
baudrate: config.baudrate, | ||
}, | ||
}; | ||
serial.init(config)?; | ||
|
@@ -518,6 +519,7 @@ | |
guard: PeripheralGuard, | ||
rts_pin: PinGuard, | ||
tx_pin: PinGuard, | ||
baudrate: u32, | ||
} | ||
|
||
/// UART (Receive) | ||
|
@@ -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) | ||
} | ||
} | ||
|
@@ -660,6 +663,7 @@ | |
guard: self.guard, | ||
rts_pin: self.rts_pin, | ||
tx_pin: self.tx_pin, | ||
baudrate: self.baudrate, | ||
} | ||
} | ||
} | ||
|
@@ -682,6 +686,7 @@ | |
guard: self.guard, | ||
rts_pin: self.rts_pin, | ||
tx_pin: self.tx_pin, | ||
baudrate: self.baudrate, | ||
} | ||
} | ||
|
||
|
@@ -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) { | ||
zpg6 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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 | ||
|
@@ -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
|
||
// wait | ||
} | ||
} | ||
|
||
Comment on lines
975
to
1014
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bugadani - I added Do you approve of this? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 It would be nice to have a |
||
/// Reconfigures the driver to operate in [`Async`] mode. | ||
#[instability::unstable] | ||
pub fn into_async(self) -> UartRx<'d, Async> { | ||
|
@@ -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, | ||
|
@@ -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. | ||
|
@@ -2271,6 +2328,7 @@ | |
GlitchDetected, | ||
FrameError, | ||
ParityError, | ||
BreakDetected, | ||
} | ||
|
||
fn rx_event_check_for_error(events: EnumSet<RxEvent>) -> Result<(), RxError> { | ||
|
@@ -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, | ||
} | ||
} | ||
|
||
|
@@ -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), | ||
}; | ||
|
@@ -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); | ||
} | ||
|
@@ -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(), | ||
}; | ||
|
@@ -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), | ||
|
@@ -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; | ||
} | ||
|
@@ -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(), | ||
|
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"] |
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 |
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); | ||
}); | ||
} |
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"] |
Uh oh!
There was an error while loading. Please reload this page.