diff --git a/usart_async_buffered_generalized2/Cargo.lock b/usart_async_buffered_generalized2/Cargo.lock index 32ec783..b595c7c 100644 --- a/usart_async_buffered_generalized2/Cargo.lock +++ b/usart_async_buffered_generalized2/Cargo.lock @@ -32,6 +32,33 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "async_uart" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "defmt 1.0.1", + "defmt-rtt", + "defmt-test", + "embassy-executor", + "embassy-futures", + "embassy-stm32", + "embassy-sync", + "embassy-time", + "embassy-usb", + "embedded-graphics", + "embedded-hal 1.0.0", + "embedded-io", + "embedded-io-async", + "heapless 0.9.1", + "micromath", + "panic-halt", + "panic-probe", + "static_cell", + "tinybmp", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -137,6 +164,15 @@ dependencies = [ "syn 2.0.107", ] +[[package]] +name = "cortex-m-semihosting" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c23234600452033cc77e4b761e740e02d2c4168e11dbf36ab14a0f58973592b0" +dependencies = [ + "cortex-m", +] + [[package]] name = "critical-section" version = "1.2.0" @@ -229,6 +265,29 @@ dependencies = [ "defmt 1.0.1", ] +[[package]] +name = "defmt-test" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24076cc7203c365e7febfcec15d6667a9ef780bd2c5fd3b2a197400df78f299b" +dependencies = [ + "cortex-m-rt", + "cortex-m-semihosting", + "defmt 1.0.1", + "defmt-test-macros", +] + +[[package]] +name = "defmt-test-macros" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5520fd36862f281c026abeaab153ebbc001717c29a9b8e5ba9704d8f3a879d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.107", +] + [[package]] name = "document-features" version = "0.2.11" @@ -616,32 +675,6 @@ dependencies = [ "pin-utils", ] -[[package]] -name = "hal_rng" -version = "0.1.0" -dependencies = [ - "cortex-m", - "cortex-m-rt", - "defmt 1.0.1", - "defmt-rtt", - "embassy-executor", - "embassy-futures", - "embassy-stm32", - "embassy-sync", - "embassy-time", - "embassy-usb", - "embedded-graphics", - "embedded-hal 1.0.0", - "embedded-io", - "embedded-io-async", - "heapless 0.9.1", - "micromath", - "panic-halt", - "panic-probe", - "static_cell", - "tinybmp", -] - [[package]] name = "hash32" version = "0.3.1" diff --git a/usart_async_buffered_generalized2/Cargo.toml b/usart_async_buffered_generalized2/Cargo.toml index fce9fec..4f40948 100644 --- a/usart_async_buffered_generalized2/Cargo.toml +++ b/usart_async_buffered_generalized2/Cargo.toml @@ -1,6 +1,6 @@ [package] authors = ["Priec "] -name = "hal_rng" +name = "async_uart" edition = "2024" version = "0.1.0" @@ -28,3 +28,6 @@ defmt = "1.0.1" static_cell = "2.1.1" embedded-io-async = "0.6.0" embedded-io = "0.6.1" + +[dev-dependencies] +defmt-test = "0.4.0" diff --git a/usart_async_buffered_generalized2/src/bin/main.rs b/usart_async_buffered_generalized2/src/bin/main.rs index 2fd39e0..c3788c6 100644 --- a/usart_async_buffered_generalized2/src/bin/main.rs +++ b/usart_async_buffered_generalized2/src/bin/main.rs @@ -14,19 +14,7 @@ use {defmt_rtt as _, panic_probe as _}; use embassy_futures::select::{select, Either}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::pipe::Pipe; - -const TX_PIPE_CAP: usize = 1024; -const RX_PIPE_CAP: usize = 1024; - -// ISR RX ring capacity = RX_BUF len -const ISR_RX_BUF_CAP: usize = 256; - -// Yield 1/2 the time it takes to fill ISR RX ring. -const YIELD_MARGIN_NUM: u32 = 1; -const YIELD_MARGIN_DEN: u32 = 2; - -// Ensure RX_PIPE_CAP can hold this. -const WORST_MAIN_LATENCY_MS: u32 = 20; +use async_uart::safety::{preflight_and_suggest_yield_period, RX_PIPE_CAP, TX_PIPE_CAP}; static UART_TX: Pipe = Pipe::new(); static UART_RX: Pipe = Pipe::new(); @@ -45,7 +33,7 @@ async fn uart_task(mut uart: BufferedUart<'static>) { // Wait for either RX or TX events. let rx_fut = uart.read(&mut rx_byte); let tx_fut = async { - // Wait until there's outgoing data in TX pipe + // Until there's outgoing data in TX pipe let n = UART_TX.read(&mut tx_buf).await; n }; @@ -66,43 +54,6 @@ async fn uart_task(mut uart: BufferedUart<'static>) { } } -fn preflight_and_suggest_yield_period(baud: u32) -> Duration { - // Approx bytes per second for 8N1 (10 bits per byte on the wire) - let bytes_per_sec = (baud / 10).max(1); - - // Time until ISR RX ring fills, in microseconds. - let t_fill_us = (ISR_RX_BUF_CAP as u64) * 1_000_000u64 / (bytes_per_sec as u64); - - // Choose a yield period as a fraction of t_fill. - let yield_us = (t_fill_us as u64) - .saturating_mul(YIELD_MARGIN_NUM as u64) - / (YIELD_MARGIN_DEN as u64); - - // Verify RX pipe can absorb a worst-case app latency so uart_task - // can always forward without dropping when it runs. - let required_rx_pipe = (bytes_per_sec as u64) * (WORST_MAIN_LATENCY_MS as u64) / 1000; - - if (RX_PIPE_CAP as u64) < required_rx_pipe { - core::panic!( - "RX pipe too small: have {}B, need >= {}B for {}ms at {} bps", - RX_PIPE_CAP, required_rx_pipe, WORST_MAIN_LATENCY_MS, baud - ); - } - - info!( - "Preflight: baud={}, rx_isr={}B, rx_pipe={}B, bytes/s={}, t_fill_us={}, yield_us={}", - baud, - ISR_RX_BUF_CAP, - RX_PIPE_CAP, - bytes_per_sec, - t_fill_us, - yield_us - ); - - // Never choose zero. - Duration::from_micros(yield_us.max(1) as u64) -} - #[embassy_executor::main] async fn main(spawner: Spawner) { info!("tititititi"); @@ -138,7 +89,11 @@ async fn main(spawner: Spawner) { // Poll RX pipe for new data (non-blocking) if let Ok(n) = UART_RX.try_read(&mut rx_buf) { if n > 0 { - info!("RX got: {:?}", &rx_buf[..n]); + if let Ok(s) = core::str::from_utf8(&rx_buf[..n]) { + info!("RX got: {}", s); + } else { + info!("RX got (non‑utf8): {:?}", &rx_buf[..n]); + } } } @@ -146,7 +101,7 @@ async fn main(spawner: Spawner) { if Instant::now().duration_since(last_yield) >= yield_period { yield_now().await; last_yield = Instant::now(); - info!("Yield mf {}", counter); + // info!("Yield mf {}", counter); } // Timer::after(Duration::from_micros(1)).await; // Timer::after(Duration::from_secs(5)).await; diff --git a/usart_async_buffered_generalized2/src/lib.rs b/usart_async_buffered_generalized2/src/lib.rs index 0c9ac1a..3c7c842 100644 --- a/usart_async_buffered_generalized2/src/lib.rs +++ b/usart_async_buffered_generalized2/src/lib.rs @@ -1 +1,2 @@ #![no_std] +pub mod safety; diff --git a/usart_async_buffered_generalized2/src/safety.rs b/usart_async_buffered_generalized2/src/safety.rs new file mode 100644 index 0000000..ad12a1d --- /dev/null +++ b/usart_async_buffered_generalized2/src/safety.rs @@ -0,0 +1,57 @@ +// src/safety.rs +use defmt::info; +use embassy_time::Duration; + +// ISR RX ring capacity = RX_BUF len +const ISR_RX_BUF_CAP: usize = 256; +// Yield 1/2 the time it takes to fill ISR RX ring. +const YIELD_MARGIN_NUM: u32 = 1; +const YIELD_MARGIN_DEN: u32 = 2; +// Ensure RX_PIPE_CAP can hold this. +const WORST_MAIN_LATENCY_MS: u32 = 20; + +pub const TX_PIPE_CAP: usize = 1024; +pub const RX_PIPE_CAP: usize = 1024; + + + +/// Perform safety checks and compute yield timing to avoid buffer overflow. +/// +/// # Panics +/// Panics if pipe capacities are too small for the configured baud. +pub fn preflight_and_suggest_yield_period(baud: u32) -> Duration { + // Approx bytes per second for 8N1 (10 bits per byte on the wire) + let bytes_per_sec = (baud / 10).max(1); + + // Time until ISR RX ring fills, in microseconds. + let t_fill_us = (ISR_RX_BUF_CAP as u64) * 1_000_000u64 / (bytes_per_sec as u64); + + // Choose a yield period as a fraction of t_fill. + let yield_us = (t_fill_us as u64) + .saturating_mul(YIELD_MARGIN_NUM as u64) + / (YIELD_MARGIN_DEN as u64); + + // Verify RX pipe can absorb a worst-case app latency so uart_task + // can always forward without dropping when it runs. + let required_rx_pipe = (bytes_per_sec as u64) * (WORST_MAIN_LATENCY_MS as u64) / 1000; + + if (RX_PIPE_CAP as u64) < required_rx_pipe { + core::panic!( + "RX pipe too small: have {}B, need >= {}B for {}ms at {} bps", + RX_PIPE_CAP, required_rx_pipe, WORST_MAIN_LATENCY_MS, baud + ); + } + + info!( + "Preflight: baud={}, rx_isr={}B, rx_pipe={}B, bytes/s={}, t_fill_us={}, yield_us={}", + baud, + ISR_RX_BUF_CAP, + RX_PIPE_CAP, + bytes_per_sec, + t_fill_us, + yield_us + ); + + // Never choose zero. + Duration::from_micros(yield_us.max(1) as u64) +} diff --git a/usart_async_buffered_generalized2/tests/integration.rs b/usart_async_buffered_generalized2/tests/integration.rs index 1f33de6..ee17e36 100644 --- a/usart_async_buffered_generalized2/tests/integration.rs +++ b/usart_async_buffered_generalized2/tests/integration.rs @@ -1,8 +1,6 @@ #![no_std] #![no_main] -use stm32u5_blinky as _; // memory layout + panic handler - // See https://crates.io/crates/defmt-test/0.3.0 for more documentation (e.g. about the 'state' // feature) #[defmt_test::tests]