diff --git a/usart_async_buffered_generalized2/src/bin/main.rs b/usart_async_buffered_generalized2/src/bin/main.rs index 02694dd..2fd39e0 100644 --- a/usart_async_buffered_generalized2/src/bin/main.rs +++ b/usart_async_buffered_generalized2/src/bin/main.rs @@ -1,18 +1,16 @@ // src/bin/main.rs #![no_std] #![no_main] - use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::peripherals; use embassy_stm32::usart::{BufferedInterruptHandler, BufferedUart, Config}; use embedded_io_async::{Read, Write}; -use embassy_time::{Timer, Duration}; +use embassy_time::{Timer, Duration, Instant}; use static_cell::StaticCell; use embassy_futures::yield_now; 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; @@ -20,7 +18,16 @@ use embassy_sync::pipe::Pipe; const TX_PIPE_CAP: usize = 1024; const RX_PIPE_CAP: usize = 1024; -// Two pipes: one for RX, one for TX. +// 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; + static UART_TX: Pipe = Pipe::new(); static UART_RX: Pipe = Pipe::new(); @@ -34,7 +41,6 @@ bind_interrupts!( async fn uart_task(mut uart: BufferedUart<'static>) { let mut rx_byte = [0u8; 1]; let mut tx_buf = [0u8; 64]; - loop { // Wait for either RX or TX events. let rx_fut = uart.read(&mut rx_byte); @@ -43,17 +49,15 @@ async fn uart_task(mut uart: BufferedUart<'static>) { let n = UART_TX.read(&mut tx_buf).await; n }; - match select(rx_fut, tx_fut).await { // Incoming data from UART hardware Either::First(res) => { if let Ok(_) = res { - // Forward to RX pipe (non-blocking) - let _ = UART_RX.try_write(&rx_byte); + // Forward to RX pipe + let _ = UART_RX.write(&rx_byte).await; let _ = UART_TX.try_write(&rx_byte); } } - // Outgoing data waiting in TX pipe Either::Second(n) => { unwrap!(uart.write(&tx_buf[..n]).await); @@ -62,19 +66,57 @@ 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"); let p = embassy_stm32::init(Default::default()); - static TX_BUF: StaticCell<[u8; 256]> = StaticCell::new(); static RX_BUF: StaticCell<[u8; 256]> = StaticCell::new(); let tx_buf = TX_BUF.init([0; 256]); let rx_buf = RX_BUF.init([0; 256]); - let mut cfg = Config::default(); cfg.baudrate = 230_400; + // Call preflight and get the computed yield period + let yield_period = preflight_and_suggest_yield_period(cfg.baudrate); + let usart = BufferedUart::new( p.USART1, p.PA10, // RX @@ -84,24 +126,15 @@ async fn main(spawner: Spawner) { Irqs, cfg, ).unwrap(); - info!("starting uart task"); spawner.spawn(uart_task(usart)).unwrap(); let mut counter: u32 = 0; let mut rx_buf = [0u8; 64]; + let mut last_yield = Instant::now(); loop { counter = counter.wrapping_add(1); - - if counter % 10000 == 0 { - info!("CPU busy {}", counter); - } - if counter % 100000 == 0 { - let _ = UART_TX.try_write(b"Hello\r\n"); - info!("Queued Hello"); - } - // Poll RX pipe for new data (non-blocking) if let Ok(n) = UART_RX.try_read(&mut rx_buf) { if n > 0 { @@ -109,10 +142,13 @@ async fn main(spawner: Spawner) { } } - if counter % 100000 == 0 { + // Guaranteed to yield before ISR RX buffer can overflow + if Instant::now().duration_since(last_yield) >= yield_period { yield_now().await; + last_yield = Instant::now(); + info!("Yield mf {}", counter); } // Timer::after(Duration::from_micros(1)).await; - Timer::after(Duration::from_secs(5)).await; + // Timer::after(Duration::from_secs(5)).await; } }