//! 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, } /// Terminal result from acknowledging the USART1 receive interrupt. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ReceiveInterrupt { /// One received byte was removed from the UART data register. Byte(u8), /// A serial transport fault invalidated the current receive stream. Fault, /// This USART1 interrupt did not have a receive event pending. NotPending, } /// Owns configured full-duplex 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 and receive 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() .re() .set_bit() .rxneie() .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 } /// Acknowledges one USART1 receive interrupt without accessing TX state. /// /// The board invokes this only from the `USART1` interrupt. USART1 TX DMA /// owns its disjoint data register and DMA stream, so this static route may /// safely acknowledge RX while that transfer is active. pub fn acknowledge_receive_interrupt() -> ReceiveInterrupt { // SAFETY: the board binds USART1 exactly once and this function touches // only its receive/error registers, never the TX DMA configuration. let peripheral = unsafe { &*stm32f722::USART1::ptr() }; let status = peripheral.isr().read(); let fault = status.pe().is_error() || status.fe().is_error() || status.nf().is_noise() || status.ore().is_overrun(); if status.rxne().is_data_ready() { let byte = peripheral.rdr().read().rdr().bits() as u8; if fault { clear_receive_errors(peripheral); ReceiveInterrupt::Fault } else { ReceiveInterrupt::Byte(byte) } } else if fault { clear_receive_errors(peripheral); ReceiveInterrupt::Fault } else { ReceiveInterrupt::NotPending } } } fn clear_receive_errors(peripheral: &stm32f722::usart1::RegisterBlock) { peripheral.icr().write(|write| { write .pecf() .clear() .fecf() .clear() .ncf() .clear() .orecf() .clear() }); } 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)); } }