From 4698877f8f033b8fb84002f6f8d9cd80c4e050bc Mon Sep 17 00:00:00 2001 From: Zach Grimaldi Date: Mon, 30 Dec 2024 11:57:19 -0500 Subject: [PATCH] feat: uart tx software break --- esp-hal/src/uart.rs | 53 +++++++++++++++++++++ examples/src/bin/uart_send_break.rs | 50 ++++++++++++++++++++ examples/src/bin/uart_send_break_async.rs | 57 +++++++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 examples/src/bin/uart_send_break.rs create mode 100644 examples/src/bin/uart_send_break_async.rs diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index cc5f529cd0..2fbf3e9098 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -754,6 +754,26 @@ where } } + /// 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) { + // Invert the TX line + self.register_block() + .conf0() + .modify(|_, w| w.txd_inv().bit(true)); + + // 1 bit time in microseconds = 1_000_000 / baudrate + // TODO: Proper baudrate retrieval! + let bit_period_us: u32 = 1_000_000 / 19200; + crate::rom::ets_delay_us(bit_period_us * bits); + + // Revert the TX line + self.register_block() + .conf0() + .modify(|_, w| w.txd_inv().bit(false)); + } + /// Checks if the TX line is idle for this UART instance. /// /// Returns `true` if the transmit line is idle, meaning no data is @@ -1252,6 +1272,11 @@ where self.tx.flush() } + /// Sends a break signal for a specified duration + pub fn send_break(&mut self, bits: u32) { + self.tx.send_break(bits) + } + /// Read a byte from the UART pub fn read_byte(&mut self) -> nb::Result { self.rx.read_byte() @@ -1802,6 +1827,11 @@ where self.tx.write_async(words).await } + /// Asynchronously sends a break signal. + pub async fn send_break_async(&mut self, bits: u32) { + self.tx.send_break_async(bits).await; + } + /// Asynchronously flushes the UART transmit buffer. pub async fn flush_async(&mut self) -> Result<(), Error> { self.tx.flush_async().await @@ -1856,6 +1886,29 @@ where Ok(()) } + + /// Asynchronously sends a break signal. + /// + /// This function sends a break signal on the UART TX line. The break + /// duration is specified in bit time, i.e. the time it takes to send + /// one bit at the current baudrate. + pub async fn send_break_async(&mut self, bits: u32) { + // Invert the TX line + self.register_block() + .conf0() + .modify(|_, w| w.txd_inv().bit(true)); + + // 1 bit time in microseconds = 1_000_000 / baudrate + // TODO: Proper baudrate retrieval! + let bit_period_us: u32 = 1_000_000 / 19200; + // TODO: Proper async delay! + crate::rom::ets_delay_us(bit_period_us * bits); + + // Revert the TX line + self.register_block() + .conf0() + .modify(|_, w| w.txd_inv().bit(false)); + } } impl UartRx<'_, Async, T> diff --git a/examples/src/bin/uart_send_break.rs b/examples/src/bin/uart_send_break.rs new file mode 100644 index 0000000000..cf0ce86391 --- /dev/null +++ b/examples/src/bin/uart_send_break.rs @@ -0,0 +1,50 @@ +//! Example of sending a software break signal from a UART in +//! Blocking mode. +//! +//! The following wiring is assumed: +//! - TEST IO PIN => GPIO2 +//! - TX => GPIO17 +//! - RX => GPIO16 +//% CHIPS: esp32 + +#![no_std] +#![no_main] + +use esp_backtrace as _; +use esp_hal::{ + delay::Delay, + entry, + gpio::{Level, Output}, + uart::{Config as UartConfig, DataBits, StopBits, Uart}, +}; + +#[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); + + let mut uart = Uart::new( + peripherals.UART1, + uart_config, + peripherals.GPIO16, // RX + peripherals.GPIO17, // TX + ) + .expect("Failed to initialize UART"); + + let delay = Delay::new(); + + // Used to toggle an output pin for comparing its timing with + // the TX line on an oscilloscope. It's also the LED pin. + let mut test_io_pin = Output::new(peripherals.GPIO2, Level::Low); + + loop { + test_io_pin.toggle(); + uart.send_break(19200); // 19200 bits at 19200bps = 1 second + test_io_pin.toggle(); + delay.delay_millis(1000); + } +} diff --git a/examples/src/bin/uart_send_break_async.rs b/examples/src/bin/uart_send_break_async.rs new file mode 100644 index 0000000000..56bfb8bdec --- /dev/null +++ b/examples/src/bin/uart_send_break_async.rs @@ -0,0 +1,57 @@ +//! Example of sending a software break signal from a UART in +//! Async mode. +//! +//! The following wiring is assumed: +//! - TEST IO PIN => GPIO2 +//! - TX => GPIO17 +//! - RX => GPIO16 +//% CHIPS: esp32 +//% FEATURES: embassy embassy-generic-timers esp-hal/unstable + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_time::Timer; +use esp_backtrace as _; +use esp_hal::{ + delay::Delay, + gpio::{Level, Output}, + timer::timg::TimerGroup, + 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 timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); + + let uart_config = UartConfig::default() + .baudrate(19200) + .data_bits(DataBits::DataBits8) + .parity_none() + .stop_bits(StopBits::Stop1); + + let mut uart = Uart::new( + peripherals.UART1, + uart_config, + peripherals.GPIO16, // RX + peripherals.GPIO17, // TX + ) + .expect("Failed to initialize UART") + .into_async(); + + let delay = Delay::new(); + + // Used to toggle an output pin for comparing its timing with + // the TX line on an oscilloscope. It's also the LED pin. + let mut test_io_pin = Output::new(peripherals.GPIO2, Level::Low); + + loop { + test_io_pin.toggle(); + uart.send_break_async(19200).await; // 19200 bits at 19200bps = 1 second + test_io_pin.toggle(); + Timer::after_millis(1000).await; + } +}