diff --git a/libs/kaptan-stm32f7/src/stm32f722/usart.rs b/libs/kaptan-stm32f7/src/stm32f722/usart.rs new file mode 100644 index 0000000..df73f54 --- /dev/null +++ b/libs/kaptan-stm32f7/src/stm32f722/usart.rs @@ -0,0 +1,84 @@ +//! USART1 transmit configuration for STM32F722. + +use stm32f7::stm32f722; + +/// UART baud-rate configuration error. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum BaudRateError { + /// Zero is not a valid baud rate. + Zero, + /// The requested divisor cannot fit USART BRR. + OutOfRange, +} + +/// Owns configured transmit-only USART1 hardware. +/// +/// Pin alternate-function setup remains board-owned because it depends on the +/// schematic. Configure pins before constructing this type. +pub struct Usart1Tx { + peripheral: stm32f722::USART1, +} + +impl Usart1Tx { + /// Enables USART1 and configures 8N1 transmit-only operation. + pub fn new( + rcc: &stm32f722::RCC, + peripheral: stm32f722::USART1, + peripheral_clock_hz: u32, + baud: u32, + ) -> Result { + let brr = calculate_brr(peripheral_clock_hz, baud)?; + + rcc.apb2rstr() + .modify(|_, write| write.usart1rst().set_bit()); + rcc.apb2rstr() + .modify(|_, write| write.usart1rst().clear_bit()); + rcc.apb2enr().modify(|_, write| write.usart1en().set_bit()); + peripheral.brr().write(|write| { + // SAFETY: `brr` is validated to fit the complete BRR register. + unsafe { write.bits(u32::from(brr)) } + }); + peripheral + .cr1() + .write(|write| write.te().set_bit().ue().set_bit()); + peripheral.cr2().write(|write| write.stop().stop1()); + + Ok(Self { peripheral }) + } + + pub(crate) fn enable_dma_transmit(&self) { + self.peripheral.cr3().write(|write| write.dmat().set_bit()); + } + + pub(crate) fn transmit_data_register_address(&self) -> u32 { + self.peripheral.tdr().as_ptr() as u32 + } +} + +const fn calculate_brr(peripheral_clock_hz: u32, baud: u32) -> Result { + if baud == 0 { + return Err(BaudRateError::Zero); + } + + let divisor = (peripheral_clock_hz + (baud / 2)) / baud; + if divisor == 0 || divisor > u16::MAX as u32 { + Err(BaudRateError::OutOfRange) + } else { + Ok(divisor as u16) + } +} + +#[cfg(test)] +mod tests { + use super::{BaudRateError, calculate_brr}; + + #[test] + fn calculates_reset_clock_brr() { + assert_eq!(calculate_brr(16_000_000, 115_200), Ok(139)); + } + + #[test] + fn rejects_zero_baud() { + assert_eq!(calculate_brr(16_000_000, 0), Err(BaudRateError::Zero)); + } +}