From 9f7565440a224f5bc2a91e3f397ec8dfaec8377f Mon Sep 17 00:00:00 2001 From: Alex Johnson Date: Mon, 26 Jun 2023 18:56:32 +0300 Subject: [PATCH] Async serial uart read (#620) * implement embassy async uart read * Add embassy async read support for uart * changes based on review * fix CI failures * change review #2 * fixed re-opened PR number * changes review no.3 --------- Co-authored-by: Scott Mabin --- CHANGELOG.md | 1 + esp-hal-common/src/uart.rs | 137 ++++++++++++++++++++----- esp32-hal/Cargo.toml | 1 + esp32-hal/examples/embassy_serial.rs | 75 +++++++++++++- esp32c2-hal/Cargo.toml | 1 + esp32c2-hal/examples/embassy_serial.rs | 75 +++++++++++++- esp32c3-hal/Cargo.toml | 1 + esp32c3-hal/examples/embassy_serial.rs | 75 +++++++++++++- esp32c6-hal/Cargo.toml | 1 + esp32c6-hal/examples/embassy_serial.rs | 75 +++++++++++++- esp32h2-hal/Cargo.toml | 1 + esp32h2-hal/examples/embassy_serial.rs | 75 +++++++++++++- esp32s2-hal/Cargo.toml | 1 + esp32s2-hal/examples/embassy_serial.rs | 75 +++++++++++++- esp32s3-hal/Cargo.toml | 1 + esp32s3-hal/examples/embassy_serial.rs | 75 +++++++++++++- 16 files changed, 608 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 294918d89..2bb283273 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix rom::crc docs - Add octal PSRAM support for ESP32-S3 (#610) - Add MD5 functions from ESP ROM (#618) +- Add embassy async `read` support for `uart` (#620) ### Changed diff --git a/esp-hal-common/src/uart.rs b/esp-hal-common/src/uart.rs index 0a5e96491..e4ccb0e8b 100644 --- a/esp-hal-common/src/uart.rs +++ b/esp-hal-common/src/uart.rs @@ -19,7 +19,10 @@ const UART_FIFO_SIZE: u16 = 128; /// Custom serial error type #[derive(Debug)] -pub enum Error {} +pub enum Error { + #[cfg(feature = "async")] + ReadBufferFull, +} /// UART configuration pub mod config { @@ -994,11 +997,15 @@ mod asynch { use core::task::Poll; use cfg_if::cfg_if; + use embassy_futures::select::select; use embassy_sync::waitqueue::AtomicWaker; use procmacros::interrupt; use super::{Error, Instance}; - use crate::{uart::UART_FIFO_SIZE, Uart}; + use crate::{ + uart::{RegisterBlock, UART_FIFO_SIZE}, + Uart, + }; cfg_if! { if #[cfg(all(uart0, uart1, uart2))] { @@ -1016,6 +1023,8 @@ mod asynch { pub(crate) enum Event { TxDone, TxFiFoEmpty, + RxFifoFull, + RxCmdCharDetected, } pub(crate) struct UartFuture<'a, T: Instance> { @@ -1034,6 +1043,14 @@ mod asynch { .register_block() .int_ena .modify(|_, w| w.txfifo_empty_int_ena().set_bit()), + Event::RxFifoFull => instance + .register_block() + .int_ena + .modify(|_, w| w.rxfifo_full_int_ena().set_bit()), + Event::RxCmdCharDetected => instance + .register_block() + .int_ena + .modify(|_, w| w.at_cmd_char_det_int_ena().set_bit()), } Self { event, instance } @@ -1055,6 +1072,20 @@ mod asynch { .read() .txfifo_empty_int_ena() .bit_is_clear(), + Event::RxFifoFull => self + .instance + .register_block() + .int_ena + .read() + .rxfifo_full_int_ena() + .bit_is_clear(), + Event::RxCmdCharDetected => self + .instance + .register_block() + .int_ena + .read() + .at_cmd_char_det_int_ena() + .bit_is_clear(), } } } @@ -1079,11 +1110,42 @@ mod asynch { where T: Instance, { - async fn write(&mut self, words: &[u8]) -> Result<(), Error> { - for chunk in words.chunks(UART_FIFO_SIZE as usize) { - for &byte in chunk { - self.write_byte(byte).unwrap() // should never fail + pub async fn read(&mut self, buf: &mut [u8]) -> Result { + let mut read_bytes = 0; + + select( + UartFuture::new(Event::RxCmdCharDetected, self.inner()), + UartFuture::new(Event::RxFifoFull, self.inner()), + ) + .await; + + while let Ok(byte) = self.read_byte() { + if read_bytes < buf.len() { + buf[read_bytes] = byte; + read_bytes += 1; + } else { + return Err(Error::ReadBufferFull); } + } + + Ok(read_bytes) + } + + async fn write(&mut self, words: &[u8]) -> Result<(), Error> { + let mut offset: usize = 0; + loop { + let mut next_offset = + offset + (UART_FIFO_SIZE - self.uart.get_tx_fifo_count()) as usize; + if next_offset > words.len() { + next_offset = words.len(); + } + for &byte in &words[offset..next_offset] { + self.write_byte(byte).unwrap(); // should never fail + } + if next_offset == words.len() { + break; + } + offset = next_offset; UartFuture::new(Event::TxFiFoEmpty, self.inner()).await; } Ok(()) @@ -1111,42 +1173,63 @@ mod asynch { } } + fn intr_handler(uart: &RegisterBlock) -> bool { + let int_ena_val = uart.int_ena.read(); + let int_raw_val = uart.int_raw.read(); + if int_ena_val.txfifo_empty_int_ena().bit_is_set() + && int_raw_val.txfifo_empty_int_raw().bit_is_set() + { + uart.int_ena.write(|w| w.txfifo_empty_int_ena().clear_bit()); + return true; + } + if int_ena_val.tx_done_int_ena().bit_is_set() && int_raw_val.tx_done_int_raw().bit_is_set() + { + uart.int_ena.write(|w| w.tx_done_int_ena().clear_bit()); + return true; + } + if int_ena_val.at_cmd_char_det_int_ena().bit_is_set() + && int_raw_val.at_cmd_char_det_int_raw().bit_is_set() + { + uart.int_clr + .write(|w| w.at_cmd_char_det_int_clr().set_bit()); + uart.int_ena + .write(|w| w.at_cmd_char_det_int_ena().clear_bit()); + return true; + } + if int_ena_val.rxfifo_full_int_ena().bit_is_set() + && int_raw_val.rxfifo_full_int_raw().bit_is_set() + { + uart.int_clr.write(|w| w.rxfifo_full_int_clr().set_bit()); + uart.int_ena.write(|w| w.rxfifo_full_int_ena().clear_bit()); + return true; + } + false + } + #[cfg(uart0)] #[interrupt] fn UART0() { let uart = unsafe { &*crate::peripherals::UART0::ptr() }; - uart.int_ena.modify(|_, w| { - w.txfifo_empty_int_ena() - .clear_bit() - .tx_done_int_ena() - .clear_bit() - }); - WAKERS[0].wake(); + if intr_handler(uart) { + WAKERS[0].wake(); + } } #[cfg(uart1)] #[interrupt] fn UART1() { let uart = unsafe { &*crate::peripherals::UART1::ptr() }; - uart.int_ena.modify(|_, w| { - w.txfifo_empty_int_ena() - .clear_bit() - .tx_done_int_ena() - .clear_bit() - }); - WAKERS[1].wake(); + if intr_handler(uart) { + WAKERS[1].wake(); + } } #[cfg(uart2)] #[interrupt] fn UART2() { let uart = unsafe { &*crate::peripherals::UART2::ptr() }; - uart.int_ena.modify(|_, w| { - w.txfifo_empty_int_ena() - .clear_bit() - .tx_done_int_ena() - .clear_bit() - }); - WAKERS[2].wake(); + if intr_handler(uart) { + WAKERS[2].wake(); + } } } diff --git a/esp32-hal/Cargo.toml b/esp32-hal/Cargo.toml index 24b99dbeb..155830f52 100644 --- a/esp32-hal/Cargo.toml +++ b/esp32-hal/Cargo.toml @@ -47,6 +47,7 @@ sha2 = { version = "0.10.6", default-features = false} smart-leds = "0.3.0" ssd1306 = "0.7.1" static_cell = "1.0.0" +heapless = "0.7.16" [features] default = ["rt", "vectored", "xtal40mhz"] diff --git a/esp32-hal/examples/embassy_serial.rs b/esp32-hal/examples/embassy_serial.rs index 088c6f09f..f10924fe2 100644 --- a/esp32-hal/examples/embassy_serial.rs +++ b/esp32-hal/examples/embassy_serial.rs @@ -1,14 +1,16 @@ //! embassy serial //! //! This is an example of running the embassy executor and asynchronously -//! writing to a uart. +//! writing to and reading from uart #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +use core::fmt::Write; + use embassy_executor::Executor; -use embassy_time::{Duration, Timer}; +use embassy_time::{with_timeout, Duration}; use esp32_hal::{ clock::ClockControl, embassy, @@ -20,18 +22,79 @@ use esp32_hal::{ Uart, }; use esp_backtrace as _; +use esp_hal_common::uart::config::AtCmdConfig; +use heapless::Vec; use static_cell::StaticCell; +// rx_fifo_full_threshold +const READ_BUF_SIZE: usize = 128; +// EOT (CTRL-D) +const AT_CMD: u8 = 0x04; + #[embassy_executor::task] async fn run(mut uart: Uart<'static, UART0>) { + // max message size to receive + // leave some extra space for AT-CMD characters + const MAX_BUFFER_SIZE: usize = 10 * READ_BUF_SIZE + 16; + // timeout read + const READ_TIMEOUT: Duration = Duration::from_secs(10); + + let mut rbuf: Vec = Vec::new(); + let mut wbuf: Vec = Vec::new(); loop { - embedded_hal_async::serial::Write::write(&mut uart, b"Hello async write!!!\r\n") + if rbuf.is_empty() { + embedded_hal_async::serial::Write::write( + &mut uart, + b"Hello async serial. Enter something ended with EOT (CTRL-D).\r\n", + ) .await .unwrap(); + } else { + wbuf.clear(); + write!(&mut wbuf, "\r\n-- received {} bytes --\r\n", rbuf.len()).unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, wbuf.as_slice()) + .await + .unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, rbuf.as_slice()) + .await + .unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, b"\r\n") + .await + .unwrap(); + } embedded_hal_async::serial::Write::flush(&mut uart) .await .unwrap(); - Timer::after(Duration::from_millis(1_000)).await; + + // set rbuf full capacity + rbuf.resize_default(rbuf.capacity()).ok(); + let mut offset = 0; + loop { + match with_timeout(READ_TIMEOUT, uart.read(&mut rbuf[offset..])).await { + Ok(r) => { + if let Ok(len) = r { + offset += len; + if offset == 0 { + rbuf.truncate(0); + break; + } + // if set_at_cmd is used than stop reading + if len < READ_BUF_SIZE { + rbuf.truncate(offset); + break; + } + } else { + // buffer is full + break; + } + } + Err(_) => { + // Timeout + rbuf.truncate(offset); + break; + } + } + } } } @@ -66,7 +129,9 @@ fn main() -> ! { #[cfg(feature = "embassy-time-timg0")] embassy::init(&clocks, timer_group0.timer0); - let uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control); + let mut uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control); + uart0.set_at_cmd(AtCmdConfig::new(None, None, None, AT_CMD, None)); + uart0.set_rx_fifo_full_threshold(READ_BUF_SIZE as u16); interrupt::enable(Interrupt::UART0, interrupt::Priority::Priority1).unwrap(); diff --git a/esp32c2-hal/Cargo.toml b/esp32c2-hal/Cargo.toml index ffbca9f71..05072e497 100644 --- a/esp32c2-hal/Cargo.toml +++ b/esp32c2-hal/Cargo.toml @@ -42,6 +42,7 @@ lis3dh-async = "0.7.0" sha2 = { version = "0.10.6", default-features = false} ssd1306 = "0.7.1" static_cell = "1.0.0" +heapless = "0.7.16" [features] default = ["rt", "vectored", "xtal40mhz"] diff --git a/esp32c2-hal/examples/embassy_serial.rs b/esp32c2-hal/examples/embassy_serial.rs index 167ceec03..461cfe048 100644 --- a/esp32c2-hal/examples/embassy_serial.rs +++ b/esp32c2-hal/examples/embassy_serial.rs @@ -1,14 +1,16 @@ //! embassy serial //! //! This is an example of running the embassy executor and asynchronously -//! writing to a uart. +//! writing to and reading from uart #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +use core::fmt::Write; + use embassy_executor::Executor; -use embassy_time::{Duration, Timer}; +use embassy_time::{with_timeout, Duration}; use esp32c2_hal::{ clock::ClockControl, embassy, @@ -20,18 +22,79 @@ use esp32c2_hal::{ Uart, }; use esp_backtrace as _; +use esp_hal_common::uart::config::AtCmdConfig; +use heapless::Vec; use static_cell::StaticCell; +// rx_fifo_full_threshold +const READ_BUF_SIZE: usize = 128; +/// EOT; CTRL-D +const AT_CMD: u8 = 0x04; + #[embassy_executor::task] async fn run(mut uart: Uart<'static, UART0>) { + // max message size to receive + // leave some extra space for AT-CMD characters + const MAX_BUFFER_SIZE: usize = 10 * READ_BUF_SIZE + 16; + // timeout read + const READ_TIMEOUT: Duration = Duration::from_secs(10); + + let mut rbuf: Vec = Vec::new(); + let mut wbuf: Vec = Vec::new(); loop { - embedded_hal_async::serial::Write::write(&mut uart, b"Hello async write!!!\r\n") + if rbuf.is_empty() { + embedded_hal_async::serial::Write::write( + &mut uart, + b"Hello async serial. Enter something ended with EOT (CTRL-D).\r\n", + ) .await .unwrap(); + } else { + wbuf.clear(); + write!(&mut wbuf, "\r\n-- received {} bytes --\r\n", rbuf.len()).unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, wbuf.as_slice()) + .await + .unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, rbuf.as_slice()) + .await + .unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, b"\r\n") + .await + .unwrap(); + } embedded_hal_async::serial::Write::flush(&mut uart) .await .unwrap(); - Timer::after(Duration::from_millis(1_000)).await; + + // set rbuf full capacity + rbuf.resize_default(rbuf.capacity()).ok(); + let mut offset = 0; + loop { + match with_timeout(READ_TIMEOUT, uart.read(&mut rbuf[offset..])).await { + Ok(r) => { + if let Ok(len) = r { + offset += len; + if offset == 0 { + rbuf.truncate(0); + break; + } + // if set_at_cmd is used than stop reading + if len < READ_BUF_SIZE { + rbuf.truncate(offset); + break; + } + } else { + // buffer is full + break; + } + } + Err(_) => { + // Timeout + rbuf.truncate(offset); + break; + } + } + } } } @@ -66,7 +129,9 @@ fn main() -> ! { #[cfg(feature = "embassy-time-timg0")] embassy::init(&clocks, timer_group0.timer0); - let uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control); + let mut uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control); + uart0.set_at_cmd(AtCmdConfig::new(None, None, None, AT_CMD, None)); + uart0.set_rx_fifo_full_threshold(READ_BUF_SIZE as u16); interrupt::enable(Interrupt::UART0, interrupt::Priority::Priority1).unwrap(); diff --git a/esp32c3-hal/Cargo.toml b/esp32c3-hal/Cargo.toml index a1e29aace..f96863e55 100644 --- a/esp32c3-hal/Cargo.toml +++ b/esp32c3-hal/Cargo.toml @@ -48,6 +48,7 @@ sha2 = { version = "0.10.6", default-features = false} smart-leds = "0.3.0" ssd1306 = "0.7.1" static_cell = "1.0.0" +heapless = "0.7.16" [features] default = ["rt", "vectored", "esp-hal-common/rv-zero-rtc-bss"] diff --git a/esp32c3-hal/examples/embassy_serial.rs b/esp32c3-hal/examples/embassy_serial.rs index 75047bc03..12bb7c57c 100644 --- a/esp32c3-hal/examples/embassy_serial.rs +++ b/esp32c3-hal/examples/embassy_serial.rs @@ -1,14 +1,16 @@ //! embassy serial //! //! This is an example of running the embassy executor and asynchronously -//! writing to a uart. +//! writing to and reading from uart #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +use core::fmt::Write; + use embassy_executor::Executor; -use embassy_time::{Duration, Timer}; +use embassy_time::{with_timeout, Duration}; use esp32c3_hal::{ clock::ClockControl, embassy, @@ -20,18 +22,79 @@ use esp32c3_hal::{ Uart, }; use esp_backtrace as _; +use esp_hal_common::uart::config::AtCmdConfig; +use heapless::Vec; use static_cell::StaticCell; +// rx_fifo_full_threshold +const READ_BUF_SIZE: usize = 128; +// EOT (CTRL-D) +const AT_CMD: u8 = 0x04; + #[embassy_executor::task] async fn run(mut uart: Uart<'static, UART0>) { + // max message size to receive + // leave some extra space for AT-CMD characters + const MAX_BUFFER_SIZE: usize = 10 * READ_BUF_SIZE + 16; + // timeout read + const READ_TIMEOUT: Duration = Duration::from_secs(10); + + let mut rbuf: Vec = Vec::new(); + let mut wbuf: Vec = Vec::new(); loop { - embedded_hal_async::serial::Write::write(&mut uart, b"Hello async write!!!\r\n") + if rbuf.is_empty() { + embedded_hal_async::serial::Write::write( + &mut uart, + b"Hello async serial. Enter something ended with EOT (CTRL-D).\r\n", + ) .await .unwrap(); + } else { + wbuf.clear(); + write!(&mut wbuf, "\r\n-- received {} bytes --\r\n", rbuf.len()).unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, wbuf.as_slice()) + .await + .unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, rbuf.as_slice()) + .await + .unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, b"\r\n") + .await + .unwrap(); + } embedded_hal_async::serial::Write::flush(&mut uart) .await .unwrap(); - Timer::after(Duration::from_millis(1_000)).await; + + // set rbuf full capacity + rbuf.resize_default(rbuf.capacity()).ok(); + let mut offset = 0; + loop { + match with_timeout(READ_TIMEOUT, uart.read(&mut rbuf[offset..])).await { + Ok(r) => { + if let Ok(len) = r { + offset += len; + if offset == 0 { + rbuf.truncate(0); + break; + } + // if set_at_cmd is used than stop reading + if len < READ_BUF_SIZE { + rbuf.truncate(offset); + break; + } + } else { + // buffer is full + break; + } + } + Err(_) => { + // Timeout + rbuf.truncate(offset); + break; + } + } + } } } @@ -73,7 +136,9 @@ fn main() -> ! { #[cfg(feature = "embassy-time-timg0")] embassy::init(&clocks, timer_group0.timer0); - let uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control); + let mut uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control); + uart0.set_at_cmd(AtCmdConfig::new(None, None, None, AT_CMD, None)); + uart0.set_rx_fifo_full_threshold(READ_BUF_SIZE as u16); interrupt::enable(Interrupt::UART0, interrupt::Priority::Priority1).unwrap(); diff --git a/esp32c6-hal/Cargo.toml b/esp32c6-hal/Cargo.toml index 582debc63..3c0e49f6d 100644 --- a/esp32c6-hal/Cargo.toml +++ b/esp32c6-hal/Cargo.toml @@ -49,6 +49,7 @@ sha2 = { version = "0.10.6", default-features = false} smart-leds = "0.3.0" ssd1306 = "0.7.1" static_cell = "1.0.0" +heapless = "0.7.16" [features] default = ["rt", "vectored", "esp-hal-common/rv-zero-rtc-bss"] diff --git a/esp32c6-hal/examples/embassy_serial.rs b/esp32c6-hal/examples/embassy_serial.rs index 69b2fa621..1cac300f8 100644 --- a/esp32c6-hal/examples/embassy_serial.rs +++ b/esp32c6-hal/examples/embassy_serial.rs @@ -1,14 +1,16 @@ //! embassy serial //! //! This is an example of running the embassy executor and asynchronously -//! writing to a uart. +//! writing to and reading from uart #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +use core::fmt::Write; + use embassy_executor::Executor; -use embassy_time::{Duration, Timer}; +use embassy_time::{with_timeout, Duration}; use esp32c6_hal::{ clock::ClockControl, embassy, @@ -20,18 +22,79 @@ use esp32c6_hal::{ Uart, }; use esp_backtrace as _; +use esp_hal_common::uart::config::AtCmdConfig; +use heapless::Vec; use static_cell::StaticCell; +// rx_fifo_full_threshold +const READ_BUF_SIZE: usize = 128; +// EOT (CTRL-D) +const AT_CMD: u8 = 0x04; + #[embassy_executor::task] async fn run(mut uart: Uart<'static, UART0>) { + // max message size to receive + // leave some extra space for AT-CMD characters + const MAX_BUFFER_SIZE: usize = 10 * READ_BUF_SIZE + 16; + // timeout read + const READ_TIMEOUT: Duration = Duration::from_secs(10); + + let mut rbuf: Vec = Vec::new(); + let mut wbuf: Vec = Vec::new(); loop { - embedded_hal_async::serial::Write::write(&mut uart, b"Hello async write!!!\r\n") + if rbuf.is_empty() { + embedded_hal_async::serial::Write::write( + &mut uart, + b"Hello async serial. Enter something ended with EOT (CTRL-D).\r\n", + ) .await .unwrap(); + } else { + wbuf.clear(); + write!(&mut wbuf, "\r\n-- received {} bytes --\r\n", rbuf.len()).unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, wbuf.as_slice()) + .await + .unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, rbuf.as_slice()) + .await + .unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, b"\r\n") + .await + .unwrap(); + } embedded_hal_async::serial::Write::flush(&mut uart) .await .unwrap(); - Timer::after(Duration::from_millis(1_000)).await; + + // set rbuf full capacity + rbuf.resize_default(rbuf.capacity()).ok(); + let mut offset = 0; + loop { + match with_timeout(READ_TIMEOUT, uart.read(&mut rbuf[offset..])).await { + Ok(r) => { + if let Ok(len) = r { + offset += len; + if offset == 0 { + rbuf.truncate(0); + break; + } + // if set_at_cmd is used than stop reading + if len < READ_BUF_SIZE { + rbuf.truncate(offset); + break; + } + } else { + // buffer is full + break; + } + } + Err(_) => { + // Timeout + rbuf.truncate(offset); + break; + } + } + } } } @@ -73,7 +136,9 @@ fn main() -> ! { #[cfg(feature = "embassy-time-timg0")] embassy::init(&clocks, timer_group0.timer0); - let uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control); + let mut uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control); + uart0.set_at_cmd(AtCmdConfig::new(None, None, None, AT_CMD, None)); + uart0.set_rx_fifo_full_threshold(READ_BUF_SIZE as u16); interrupt::enable(Interrupt::UART0, interrupt::Priority::Priority1).unwrap(); diff --git a/esp32h2-hal/Cargo.toml b/esp32h2-hal/Cargo.toml index 66071d506..5c5c7cc5c 100644 --- a/esp32h2-hal/Cargo.toml +++ b/esp32h2-hal/Cargo.toml @@ -49,6 +49,7 @@ sha2 = { version = "0.10.6", default-features = false} smart-leds = "0.3.0" ssd1306 = "0.7.1" static_cell = "1.0.0" +heapless = "0.7.16" [features] default = ["rt", "vectored", "esp-hal-common/rv-zero-rtc-bss"] diff --git a/esp32h2-hal/examples/embassy_serial.rs b/esp32h2-hal/examples/embassy_serial.rs index 03b366fdc..815cde5e2 100644 --- a/esp32h2-hal/examples/embassy_serial.rs +++ b/esp32h2-hal/examples/embassy_serial.rs @@ -1,14 +1,16 @@ //! embassy serial //! //! This is an example of running the embassy executor and asynchronously -//! writing to a uart. +//! writing to and reading from uart #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +use core::fmt::Write; + use embassy_executor::Executor; -use embassy_time::{Duration, Timer}; +use embassy_time::{with_timeout, Duration}; use esp32h2_hal::{ clock::ClockControl, embassy, @@ -20,18 +22,79 @@ use esp32h2_hal::{ Uart, }; use esp_backtrace as _; +use esp_hal_common::uart::config::AtCmdConfig; +use heapless::Vec; use static_cell::StaticCell; +// rx_fifo_full_threshold +const READ_BUF_SIZE: usize = 128; +// EOT (CTRL-D) +const AT_CMD: u8 = 0x04; + #[embassy_executor::task] async fn run(mut uart: Uart<'static, UART0>) { + // max message size to receive + // leave some extra space for AT-CMD characters + const MAX_BUFFER_SIZE: usize = 10 * READ_BUF_SIZE + 16; + // timeout read + const READ_TIMEOUT: Duration = Duration::from_secs(10); + + let mut rbuf: Vec = Vec::new(); + let mut wbuf: Vec = Vec::new(); loop { - embedded_hal_async::serial::Write::write(&mut uart, b"Hello async write!!!\r\n") + if rbuf.is_empty() { + embedded_hal_async::serial::Write::write( + &mut uart, + b"Hello async serial. Enter something ended with EOT (CTRL-D).\r\n", + ) .await .unwrap(); + } else { + wbuf.clear(); + write!(&mut wbuf, "\r\n-- received {} bytes --\r\n", rbuf.len()).unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, wbuf.as_slice()) + .await + .unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, rbuf.as_slice()) + .await + .unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, b"\r\n") + .await + .unwrap(); + } embedded_hal_async::serial::Write::flush(&mut uart) .await .unwrap(); - Timer::after(Duration::from_millis(1_000)).await; + + // set rbuf full capacity + rbuf.resize_default(rbuf.capacity()).ok(); + let mut offset = 0; + loop { + match with_timeout(READ_TIMEOUT, uart.read(&mut rbuf[offset..])).await { + Ok(r) => { + if let Ok(len) = r { + offset += len; + if offset == 0 { + rbuf.truncate(0); + break; + } + // if set_at_cmd is used than stop reading + if len < READ_BUF_SIZE { + rbuf.truncate(offset); + break; + } + } else { + // buffer is full + break; + } + } + Err(_) => { + // Timeout + rbuf.truncate(offset); + break; + } + } + } } } @@ -73,7 +136,9 @@ fn main() -> ! { #[cfg(feature = "embassy-time-timg0")] embassy::init(&clocks, timer_group0.timer0); - let uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control); + let mut uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control); + uart0.set_at_cmd(AtCmdConfig::new(None, None, None, AT_CMD, None)); + uart0.set_rx_fifo_full_threshold(READ_BUF_SIZE as u16); interrupt::enable(Interrupt::UART0, interrupt::Priority::Priority1).unwrap(); diff --git a/esp32s2-hal/Cargo.toml b/esp32s2-hal/Cargo.toml index 5302423d9..468590238 100644 --- a/esp32s2-hal/Cargo.toml +++ b/esp32s2-hal/Cargo.toml @@ -50,6 +50,7 @@ ssd1306 = "0.7.1" static_cell = "1.0.0" usb-device = { version = "0.2.9" } usbd-serial = "0.1.1" +heapless = "0.7.16" [features] default = ["rt", "vectored"] diff --git a/esp32s2-hal/examples/embassy_serial.rs b/esp32s2-hal/examples/embassy_serial.rs index 5e3b013cb..49859d8c4 100644 --- a/esp32s2-hal/examples/embassy_serial.rs +++ b/esp32s2-hal/examples/embassy_serial.rs @@ -1,14 +1,16 @@ //! embassy serial //! //! This is an example of running the embassy executor and asynchronously -//! writing to a uart. +//! writing to and reading from uart #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +use core::fmt::Write; + use embassy_executor::Executor; -use embassy_time::{Duration, Timer}; +use embassy_time::{with_timeout, Duration}; use esp32s2_hal::{ clock::ClockControl, embassy, @@ -20,18 +22,79 @@ use esp32s2_hal::{ Uart, }; use esp_backtrace as _; +use esp_hal_common::uart::config::AtCmdConfig; +use heapless::Vec; use static_cell::StaticCell; +// rx_fifo_full_threshold +const READ_BUF_SIZE: usize = 128; +// EOT (CTRL-D) +const AT_CMD: u8 = 0x04; + #[embassy_executor::task] async fn run(mut uart: Uart<'static, UART0>) { + // max message size to receive + // leave some extra space for AT-CMD characters + const MAX_BUFFER_SIZE: usize = 10 * READ_BUF_SIZE + 16; + // timeout read + const READ_TIMEOUT: Duration = Duration::from_secs(10); + + let mut rbuf: Vec = Vec::new(); + let mut wbuf: Vec = Vec::new(); loop { - embedded_hal_async::serial::Write::write(&mut uart, b"Hello async write!!!\r\n") + if rbuf.is_empty() { + embedded_hal_async::serial::Write::write( + &mut uart, + b"Hello async serial. Enter something ended with EOT (CTRL-D).\r\n", + ) .await .unwrap(); + } else { + wbuf.clear(); + write!(&mut wbuf, "\r\n-- received {} bytes --\r\n", rbuf.len()).unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, wbuf.as_slice()) + .await + .unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, rbuf.as_slice()) + .await + .unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, b"\r\n") + .await + .unwrap(); + } embedded_hal_async::serial::Write::flush(&mut uart) .await .unwrap(); - Timer::after(Duration::from_millis(1_000)).await; + + // set rbuf full capacity + rbuf.resize_default(rbuf.capacity()).ok(); + let mut offset = 0; + loop { + match with_timeout(READ_TIMEOUT, uart.read(&mut rbuf[offset..])).await { + Ok(r) => { + if let Ok(len) = r { + offset += len; + if offset == 0 { + rbuf.truncate(0); + break; + } + // if set_at_cmd is used than stop reading + if len < READ_BUF_SIZE { + rbuf.truncate(offset); + break; + } + } else { + // buffer is full + break; + } + } + Err(_) => { + // Timeout + rbuf.truncate(offset); + break; + } + } + } } } @@ -66,7 +129,9 @@ fn main() -> ! { #[cfg(feature = "embassy-time-timg0")] embassy::init(&clocks, timer_group0.timer0); - let uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control); + let mut uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control); + uart0.set_at_cmd(AtCmdConfig::new(None, None, None, AT_CMD, None)); + uart0.set_rx_fifo_full_threshold(READ_BUF_SIZE as u16); interrupt::enable(Interrupt::UART0, interrupt::Priority::Priority1).unwrap(); diff --git a/esp32s3-hal/Cargo.toml b/esp32s3-hal/Cargo.toml index 9c07f7569..a226abf5b 100644 --- a/esp32s3-hal/Cargo.toml +++ b/esp32s3-hal/Cargo.toml @@ -52,6 +52,7 @@ ssd1306 = "0.7.1" static_cell = "1.0.0" usb-device = "0.2.9" usbd-serial = "0.1.1" +heapless = "0.7.16" [features] default = ["rt", "vectored"] diff --git a/esp32s3-hal/examples/embassy_serial.rs b/esp32s3-hal/examples/embassy_serial.rs index 4fcb6cc9c..08ab771cd 100644 --- a/esp32s3-hal/examples/embassy_serial.rs +++ b/esp32s3-hal/examples/embassy_serial.rs @@ -1,14 +1,16 @@ //! embassy serial //! //! This is an example of running the embassy executor and asynchronously -//! writing to a uart. +//! writing to and reading from uart #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +use core::fmt::Write; + use embassy_executor::Executor; -use embassy_time::{Duration, Timer}; +use embassy_time::{with_timeout, Duration}; use esp32s3_hal::{ clock::ClockControl, embassy, @@ -20,18 +22,79 @@ use esp32s3_hal::{ Uart, }; use esp_backtrace as _; +use esp_hal_common::uart::config::AtCmdConfig; +use heapless::Vec; use static_cell::StaticCell; +// rx_fifo_full_threshold +const READ_BUF_SIZE: usize = 128; +// EOT (CTRL-D) +const AT_CMD: u8 = 0x04; + #[embassy_executor::task] async fn run(mut uart: Uart<'static, UART0>) { + // max message size to receive + // leave some extra space for AT-CMD characters + const MAX_BUFFER_SIZE: usize = 10 * READ_BUF_SIZE + 16; + // timeout read + const READ_TIMEOUT: Duration = Duration::from_secs(10); + + let mut rbuf: Vec = Vec::new(); + let mut wbuf: Vec = Vec::new(); loop { - embedded_hal_async::serial::Write::write(&mut uart, b"Hello async write!!!\r\n") + if rbuf.is_empty() { + embedded_hal_async::serial::Write::write( + &mut uart, + b"Hello async serial. Enter something ended with EOT (CTRL-D).\r\n", + ) .await .unwrap(); + } else { + wbuf.clear(); + write!(&mut wbuf, "\r\n-- received {} bytes --\r\n", rbuf.len()).unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, wbuf.as_slice()) + .await + .unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, rbuf.as_slice()) + .await + .unwrap(); + embedded_hal_async::serial::Write::write(&mut uart, b"\r\n") + .await + .unwrap(); + } embedded_hal_async::serial::Write::flush(&mut uart) .await .unwrap(); - Timer::after(Duration::from_millis(1_000)).await; + + // set rbuf full capacity + rbuf.resize_default(rbuf.capacity()).ok(); + let mut offset = 0; + loop { + match with_timeout(READ_TIMEOUT, uart.read(&mut rbuf[offset..])).await { + Ok(r) => { + if let Ok(len) = r { + offset += len; + if offset == 0 { + rbuf.truncate(0); + break; + } + // if set_at_cmd is used than stop reading + if len < READ_BUF_SIZE { + rbuf.truncate(offset); + break; + } + } else { + // buffer is full + break; + } + } + Err(_) => { + // Timeout + rbuf.truncate(offset); + break; + } + } + } } } @@ -73,7 +136,9 @@ fn main() -> ! { #[cfg(feature = "embassy-time-timg0")] embassy::init(&clocks, timer_group0.timer0); - let uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control); + let mut uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control); + uart0.set_at_cmd(AtCmdConfig::new(None, None, None, AT_CMD, None)); + uart0.set_rx_fifo_full_threshold(READ_BUF_SIZE as u16); interrupt::enable(Interrupt::UART0, interrupt::Priority::Priority1).unwrap();