diff --git a/semestralka_2/src/bin/main.rs b/semestralka_2/src/bin/main.rs index 180a3f2..9f1d04a 100644 --- a/semestralka_2/src/bin/main.rs +++ b/semestralka_2/src/bin/main.rs @@ -5,8 +5,20 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; +use embassy_stm32::bind_interrupts; +use embassy_stm32::peripherals; +use embassy_stm32::usart::{BufferedUart, Config, BufferedInterruptHandler}; + +use static_cell::StaticCell; +use dma_gpio::config::{ + BAUD, PIPE_HW_RX, PIPE_HW_TX, +}; +use dma_gpio::hw_uart_pc::{driver::uart_task, usart1}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USART1 => BufferedInterruptHandler; +}); #[embassy_executor::main] async fn main(spawner: Spawner) { @@ -14,6 +26,24 @@ async fn main(spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("init m8"); + // HARDWARE UART to the PC + let mut cfg = Config::default(); + cfg.baudrate = BAUD; + static TX_BUF: StaticCell<[u8; 256]> = StaticCell::new(); + static RX_BUF: StaticCell<[u8; 256]> = StaticCell::new(); + let uart = BufferedUart::new( + p.USART1, + p.PA10, // RX pin + p.PA9, // TX pin + TX_BUF.init([0; 256]), + RX_BUF.init([0; 256]), + Irqs, + cfg, + ).unwrap(); + // let yield_period = usart1::setup_and_spawn(BAUD); + spawner.spawn(uart_task(uart, &PIPE_HW_TX, &PIPE_HW_RX).unwrap()); + // END OF HARDWARE UART to the PC + loop { info!("tick start"); // Timer::after(Duration::from_millis(100)).await; diff --git a/semestralka_2/src/config.rs b/semestralka_2/src/config.rs new file mode 100644 index 0000000..1334716 --- /dev/null +++ b/semestralka_2/src/config.rs @@ -0,0 +1,10 @@ +// src/config.rs +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::pipe::Pipe; + +pub const BAUD: u32 = 9_600; +pub const PIPE_HW_TX_SIZE: usize = 1024; +pub const PIPE_HW_RX_SIZE: usize = 1024; + +pub static PIPE_HW_TX: Pipe = Pipe::new(); +pub static PIPE_HW_RX: Pipe = Pipe::new(); diff --git a/semestralka_2/src/hw_uart_pc/driver.rs b/semestralka_2/src/hw_uart_pc/driver.rs new file mode 100644 index 0000000..a8c7728 --- /dev/null +++ b/semestralka_2/src/hw_uart_pc/driver.rs @@ -0,0 +1,41 @@ +// src/hw_uart_pc/driver.rs +use defmt::unwrap; +use embassy_futures::select::{select, Either}; +use embassy_stm32::usart::BufferedUart; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::pipe::Pipe; +use embedded_io_async::{Read, Write}; +use crate::hw_uart_pc::safety::{RX_PIPE_CAP, TX_PIPE_CAP}; +use embassy_futures::yield_now; + +#[embassy_executor::task] +pub async fn uart_task( + mut uart: BufferedUart<'static>, + tx_pipe: &'static Pipe, + rx_pipe: &'static Pipe, +) { + let mut rx_byte = [0u8; 1]; + let mut tx_buf = [0u8; 64]; + + loop { + let rx_fut = uart.read(&mut rx_byte); + let tx_fut = async { + let n = tx_pipe.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 { + let _ = rx_pipe.write(&rx_byte).await; + } + } + // Outgoing data waiting in TX pipe + Either::Second(n) => { + unwrap!(uart.write(&tx_buf[..n]).await); + } + } + yield_now().await; + } +} diff --git a/semestralka_2/src/hw_uart_pc/mod.rs b/semestralka_2/src/hw_uart_pc/mod.rs new file mode 100644 index 0000000..22999cc --- /dev/null +++ b/semestralka_2/src/hw_uart_pc/mod.rs @@ -0,0 +1,4 @@ +// src/hw_uart_pc/mod.rs +pub mod driver; +pub mod usart1; +pub mod safety; diff --git a/semestralka_2/src/hw_uart_pc/safety.rs b/semestralka_2/src/hw_uart_pc/safety.rs new file mode 100644 index 0000000..ad12a1d --- /dev/null +++ b/semestralka_2/src/hw_uart_pc/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/semestralka_2/src/hw_uart_pc/usart1.rs b/semestralka_2/src/hw_uart_pc/usart1.rs new file mode 100644 index 0000000..92cf78d --- /dev/null +++ b/semestralka_2/src/hw_uart_pc/usart1.rs @@ -0,0 +1,12 @@ +// src/uart/usart1.rs +use defmt::info; +use embassy_time::Duration; + +use crate::hw_uart_pc::safety::preflight_and_suggest_yield_period; + +pub fn setup_and_spawn(baudrate: u32,) -> Duration { + let yield_period: Duration = preflight_and_suggest_yield_period(baudrate); + info!("HW USART1 safe"); + + yield_period +} diff --git a/semestralka_2/src/lib.rs b/semestralka_2/src/lib.rs index 1ef6109..4c89768 100644 --- a/semestralka_2/src/lib.rs +++ b/semestralka_2/src/lib.rs @@ -1,2 +1,6 @@ #![no_std] +// pub mod low_power; +// pub use low_power::*; +pub mod hw_uart_pc; +pub mod config;