From 17c205f23b49ed20c6dc6d6032c0783418c956d1 Mon Sep 17 00:00:00 2001 From: Filipriec Date: Tue, 11 Nov 2025 17:16:18 +0100 Subject: [PATCH] adding hardware uart --- semestralka_1/src/bin/main.rs | 36 +++++++++++--- semestralka_1/src/hw_uart_pc/driver.rs | 65 ++++++++++++++++++++++++++ semestralka_1/src/hw_uart_pc/mod.rs | 4 ++ semestralka_1/src/hw_uart_pc/safety.rs | 57 ++++++++++++++++++++++ semestralka_1/src/hw_uart_pc/usart1.rs | 23 +++++++++ semestralka_1/src/lib.rs | 1 + 6 files changed, 180 insertions(+), 6 deletions(-) create mode 100644 semestralka_1/src/hw_uart_pc/driver.rs create mode 100644 semestralka_1/src/hw_uart_pc/mod.rs create mode 100644 semestralka_1/src/hw_uart_pc/safety.rs create mode 100644 semestralka_1/src/hw_uart_pc/usart1.rs diff --git a/semestralka_1/src/bin/main.rs b/semestralka_1/src/bin/main.rs index 06e514f..167ceee 100644 --- a/semestralka_1/src/bin/main.rs +++ b/semestralka_1/src/bin/main.rs @@ -17,38 +17,62 @@ use dma_gpio::config::{TX_RING_BYTES, RX_RING_BYTES, PIPE_RX_SIZE}; use dma_gpio::software_uart::gpio_dma_uart_tx::tx_dma_task; use static_cell::StaticCell; use embassy_futures::yield_now; +use hw_uart_pc::usart1; +use embassy_stm32::usart::{BufferedUart, Config, BufferedInterruptHandler}; +use embassy_stm32::peripherals; use {defmt_rtt as _, panic_probe as _}; -pub const TIM6_UP_REQ: Request = 4; +bind_interrupts!(struct Irqs { + USART1 => BufferedInterruptHandler; +}); +// Software uart +pub const TIM6_UP_REQ: Request = 4; static PIPE_RX: Pipe = Pipe::new(); static TX_RING: StaticCell<[u32; TX_RING_BYTES]> = StaticCell::new(); static RX_RING: StaticCell<[u8; RX_RING_BYTES]> = StaticCell::new(); #[embassy_executor::main] async fn main(spawner: Spawner) { + info!("boot"); let p = embassy_stm32::init(Default::default()); - info!("Hehe"); + info!("init m8"); + // HARDWARE UART to the PC + + let mut cfg = Config::default(); + cfg.baudrate = 230_400; + 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 handle = usart1::setup_and_spawn(&spawner, uart, cfg.baudrate); + // END OF HARDWARE UART to the PC + + // SOFTWARE UART let _rx = Input::new(p.PA3, Pull::Up); let _tx = Output::new(p.PA2, Level::High, Speed::VeryHigh); - init_tim6_for_uart(p.TIM6, BAUD, TX_OVERSAMPLE); init_tim7_for_uart(p.TIM7, BAUD, RX_OVERSAMPLE); - dump_tim6_regs(); // Safe one-time init from StaticCell let rx_ring: &mut [u8; RX_RING_BYTES] = RX_RING.init([0; RX_RING_BYTES]); let tx_ring_mem: &mut [u32; TX_RING_BYTES] = TX_RING.init([0; TX_RING_BYTES]); - - // Spawn tasks spawner.spawn(rx_dma_task(p.GPDMA1_CH1, rx_ring, &PIPE_RX).unwrap()); // Create and start the TX DMA ring in main. // let bsrr_ptr = embassy_stm32::pac::GPIOA.bsrr().as_ptr() as *mut u32; let odr_ptr = embassy_stm32::pac::GPIOA.odr().as_ptr() as *mut u32; spawner.spawn(tx_dma_task(p.GPDMA1_CH0, odr_ptr, tx_ring_mem, &PIPE_RX).unwrap()); + // EDN OF SOFTWARE UART loop { info!("tick start"); diff --git a/semestralka_1/src/hw_uart_pc/driver.rs b/semestralka_1/src/hw_uart_pc/driver.rs new file mode 100644 index 0000000..a18fb2f --- /dev/null +++ b/semestralka_1/src/hw_uart_pc/driver.rs @@ -0,0 +1,65 @@ +// src/uart/driver.rs +use defmt::unwrap; +use embassy_executor::Spawner; +use embassy_futures::select::{select, Either}; +use embassy_stm32::usart::BufferedUart; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::pipe::Pipe; +use embassy_time::Duration; +use embedded_io_async::{Read, Write}; + +use crate::uart::safety::{RX_PIPE_CAP, TX_PIPE_CAP}; + +pub struct UartHandle { + pub tx: &'static Pipe, + pub rx: &'static Pipe, + pub yield_period: Duration, +} + +#[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 { + // Forward to RX pipe and echo to TX pipe (same behavior as before) + let _ = rx_pipe.write(&rx_byte).await; + let _ = tx_pipe.try_write(&rx_byte); + } + } + // Outgoing data waiting in TX pipe + Either::Second(n) => { + unwrap!(uart.write(&tx_buf[..n]).await); + } + } + } +} + +pub fn spawn_for( + spawner: &Spawner, + uart: BufferedUart<'static>, + tx_pipe: &'static Pipe, + rx_pipe: &'static Pipe, + yield_period: Duration, +) -> UartHandle { + spawner.spawn(uart_task(uart, tx_pipe, rx_pipe)).unwrap(); + UartHandle { + tx: tx_pipe, + rx: rx_pipe, + yield_period, + } +} diff --git a/semestralka_1/src/hw_uart_pc/mod.rs b/semestralka_1/src/hw_uart_pc/mod.rs new file mode 100644 index 0000000..1ad692c --- /dev/null +++ b/semestralka_1/src/hw_uart_pc/mod.rs @@ -0,0 +1,4 @@ +// src/uart/mod.rs +pub mod driver; +pub mod usart1; +pub mod safety; diff --git a/semestralka_1/src/hw_uart_pc/safety.rs b/semestralka_1/src/hw_uart_pc/safety.rs new file mode 100644 index 0000000..ad12a1d --- /dev/null +++ b/semestralka_1/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_1/src/hw_uart_pc/usart1.rs b/semestralka_1/src/hw_uart_pc/usart1.rs new file mode 100644 index 0000000..41a8506 --- /dev/null +++ b/semestralka_1/src/hw_uart_pc/usart1.rs @@ -0,0 +1,23 @@ +// src/uart/usart1.rs +use defmt::info; +use embassy_executor::Spawner; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::pipe::Pipe; +use embassy_time::Duration; + +use crate::uart::safety::{preflight_and_suggest_yield_period, RX_PIPE_CAP, TX_PIPE_CAP}; +use crate::uart::driver::{spawn_for, UartHandle}; + +// Static pipes and buffers +static UART1_TX_PIPE: Pipe = Pipe::new(); +static UART1_RX_PIPE: Pipe = Pipe::new(); + +pub fn setup_and_spawn( + spawner: &Spawner, + uart: embassy_stm32::usart::BufferedUart<'static>, + baudrate: u32, +) -> UartHandle { + let yield_period: Duration = preflight_and_suggest_yield_period(baudrate); + info!("USART1 initialized"); + spawn_for(spawner, uart, &UART1_TX_PIPE, &UART1_RX_PIPE, yield_period) +} diff --git a/semestralka_1/src/lib.rs b/semestralka_1/src/lib.rs index 9123142..baae530 100644 --- a/semestralka_1/src/lib.rs +++ b/semestralka_1/src/lib.rs @@ -2,3 +2,4 @@ pub mod software_uart; pub mod config; +pub mod hw_uart_pc;