diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index e203336e..255ddfd4 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -809,45 +809,57 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: Kind::Uart => (1, 0x10, 0x1_0000), }; + fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 { + // The calculation to be done to get the BRR is `mul * pclk / presc / baud` + // To do this in 32-bit only we can't multiply `mul` and `pclk` + let clock = pclk / presc; + + // The mul is applied as the last operation to prevent overflow + let brr = clock / baud * mul; + + // The BRR calculation will be a bit off because of integer rounding. + // Because we multiplied our inaccuracy with mul, our rounding now needs to be in proportion to mul. + let rounding = ((clock % baud) * mul + (baud / 2)) / baud; + + brr + rounding + } + #[cfg(not(usart_v1))] let mut over8 = false; - let mut found = None; + let mut found_brr = None; for &(presc, _presc_val) in &DIVS { - let denom = (config.baudrate * presc as u32) as u64; - let div = (pclk_freq.0 as u64 * mul + (denom / 2)) / denom; + let brr = calculate_brr(config.baudrate, pclk_freq.0, presc as u32, mul); trace!( "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", presc, - div, - div >> 4, - div & 0x0F + brr, + brr >> 4, + brr & 0x0F ); - if div < brr_min { + if brr < brr_min { #[cfg(not(usart_v1))] - if div * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { + if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { over8 = true; - let div = div as u32; - r.brr().write_value(regs::Brr(((div << 1) & !0xF) | (div & 0x07))); + r.brr().write_value(regs::Brr(((brr << 1) & !0xF) | (brr & 0x07))); #[cfg(usart_v4)] r.presc().write(|w| w.set_prescaler(_presc_val)); - found = Some(div); + found_brr = Some(brr); break; } panic!("USART: baudrate too high"); } - if div < brr_max { - let div = div as u32; - r.brr().write_value(regs::Brr(div)); + if brr < brr_max { + r.brr().write_value(regs::Brr(brr)); #[cfg(usart_v4)] r.presc().write(|w| w.set_prescaler(_presc_val)); - found = Some(div); + found_brr = Some(brr); break; } } - let div = found.expect("USART: baudrate too low"); + let brr = found_brr.expect("USART: baudrate too low"); #[cfg(not(usart_v1))] let oversampling = if over8 { "8 bit" } else { "16 bit" }; @@ -857,7 +869,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", oversampling, config.baudrate, - (pclk_freq.0 * mul as u32) / div + pclk_freq.0 / brr * mul ); r.cr2().write(|w| {