From 6494429a20f6d32e9afdd82417392bcfafc73687 Mon Sep 17 00:00:00 2001 From: Enmanuel Parache Date: Wed, 6 Nov 2024 17:02:39 -0400 Subject: [PATCH 1/5] stm32/usart: Changing baud rate --- embassy-stm32/src/usart/mod.rs | 149 +++++++++++++++++++++++++++--- examples/stm32l1/src/bin/usart.rs | 37 ++++++++ 2 files changed, 171 insertions(+), 15 deletions(-) create mode 100644 examples/stm32l1/src/bin/usart.rs diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index d2fb85ee3..b5db672f2 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -539,6 +539,12 @@ impl<'d, M: Mode> UartTx<'d, M> { pub fn send_break(&self) { send_break(&self.info.regs); } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> + { + set_baudrate(self.info, self.kernel_clock, baudrate) + } } /// Wait until transmission complete @@ -1014,6 +1020,12 @@ impl<'d, M: Mode> UartRx<'d, M> { } Ok(()) } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> + { + set_baudrate(self.info, self.kernel_clock, baudrate) + } } impl<'d, M: Mode> Drop for UartTx<'d, M> { @@ -1455,6 +1467,14 @@ impl<'d, M: Mode> Uart<'d, M> { pub fn send_break(&self) { self.tx.send_break(); } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> + { + self.tx.set_baudrate(baudrate)?; + self.rx.set_baudrate(baudrate)?; + Ok(()) + } } fn reconfigure(info: &Info, kernel_clock: Hertz, config: &Config) -> Result<(), ConfigError> { @@ -1470,6 +1490,120 @@ fn reconfigure(info: &Info, kernel_clock: Hertz, config: &Config) -> Result<(), Ok(()) } +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 +} + +fn set_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), ConfigError> { + info.interrupt.disable(); + + set_usart_baudrate(info, kernel_clock, baudrate)?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) +} + +fn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), ConfigError> { + let r = info.regs; + let kind = info.kind; + + #[cfg(not(usart_v4))] + static DIVS: [(u16, ()); 1] = [(1, ())]; + + #[cfg(usart_v4)] + static DIVS: [(u16, vals::Presc); 12] = [ + (1, vals::Presc::DIV1), + (2, vals::Presc::DIV2), + (4, vals::Presc::DIV4), + (6, vals::Presc::DIV6), + (8, vals::Presc::DIV8), + (10, vals::Presc::DIV10), + (12, vals::Presc::DIV12), + (16, vals::Presc::DIV16), + (32, vals::Presc::DIV32), + (64, vals::Presc::DIV64), + (128, vals::Presc::DIV128), + (256, vals::Presc::DIV256), + ]; + + let (mul, brr_min, brr_max) = match kind { + #[cfg(any(usart_v3, usart_v4))] + Kind::Lpuart => { + trace!("USART: Kind::Lpuart"); + (256, 0x300, 0x10_0000) + } + Kind::Uart => { + trace!("USART: Kind::Uart"); + (1, 0x10, 0x1_0000) + } + }; + + r.cr1().modify(|w| { + // disable uart + w.set_ue(false); + }); + + let mut found_brr = None; + for &(presc, _presc_val) in &DIVS { + let brr = calculate_brr(baudrate, kernel_clock.0, presc as u32, mul); + trace!( + "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", + presc, + brr, + brr >> 4, + brr & 0x0F + ); + + if brr < brr_min { + #[cfg(not(usart_v1))] + if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { + r.brr().write_value(regs::Brr(((brr << 1) & !0xF) | (brr & 0x07))); + #[cfg(usart_v4)] + r.presc().write(|w| w.set_prescaler(_presc_val)); + found_brr = Some(brr); + break; + } + return Err(ConfigError::BaudrateTooHigh); + } + + if brr < brr_max { + r.brr().write_value(regs::Brr(brr)); + #[cfg(usart_v4)] + r.presc().write(|w| w.set_prescaler(_presc_val)); + found_brr = Some(brr); + break; + } + } + + let brr = found_brr.ok_or(ConfigError::BaudrateTooLow)?; + + trace!( + "Desired baudrate: {}, actual baudrate: {}", + baudrate, + kernel_clock.0 / brr * mul + ); + + r.cr1().modify(|w| { + // enable uart + w.set_ue(true); + }); + + Ok(()) +} + fn configure( info: &Info, kernel_clock: Hertz, @@ -1515,21 +1649,6 @@ fn configure( } }; - 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 - } - // UART must be disabled during configuration. r.cr1().modify(|w| { w.set_ue(false); diff --git a/examples/stm32l1/src/bin/usart.rs b/examples/stm32l1/src/bin/usart.rs new file mode 100644 index 000000000..dba79b8b4 --- /dev/null +++ b/examples/stm32l1/src/bin/usart.rs @@ -0,0 +1,37 @@ +#![no_std] +#![no_main] + +use cortex_m_rt::entry; +use defmt::*; +use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + USART2 => usart::InterruptHandler; +}); + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let p = embassy_stm32::init(Default::default()); + + let config = Config::default(); + let mut usart = Uart::new_blocking(p.USART2, p.PA3, p.PA2, config).unwrap(); + let desired_baudrate = 9600; // Default is 115200 and 9600 is used as example + + match usart.set_baudrate(desired_baudrate) { + Ok(_) => info!("Baud rate set to {}", desired_baudrate), + Err(err) => error!("Error setting baudrate to {}: {}", desired_baudrate, err), + } + + unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); + info!("wrote Hello, starting echo"); + + let mut buf = [0u8; 1]; + loop { + unwrap!(usart.blocking_read(&mut buf)); + unwrap!(usart.blocking_write(&buf)); + } +} From def2601558dcc075fc9ee7dc13b5ab5e55fcb96e Mon Sep 17 00:00:00 2001 From: Enmanuel Parache Date: Wed, 6 Nov 2024 17:23:55 -0400 Subject: [PATCH 2/5] rustfmt --- embassy-stm32/src/usart/mod.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index b5db672f2..dc73de0c7 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -541,8 +541,7 @@ impl<'d, M: Mode> UartTx<'d, M> { } /// Set baudrate - pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> - { + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { set_baudrate(self.info, self.kernel_clock, baudrate) } } @@ -1022,8 +1021,7 @@ impl<'d, M: Mode> UartRx<'d, M> { } /// Set baudrate - pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> - { + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { set_baudrate(self.info, self.kernel_clock, baudrate) } } @@ -1469,8 +1467,7 @@ impl<'d, M: Mode> Uart<'d, M> { } /// Set baudrate - pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> - { + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { self.tx.set_baudrate(baudrate)?; self.rx.set_baudrate(baudrate)?; Ok(()) From 63298e2f7c5c31eecdf0f734e85e94ad5e423bcb Mon Sep 17 00:00:00 2001 From: Enmanuel Parache Date: Thu, 7 Nov 2024 14:43:27 -0400 Subject: [PATCH 3/5] Reduced code duplication --- embassy-stm32/src/usart/mod.rs | 183 ++++++++++++--------------------- 1 file changed, 63 insertions(+), 120 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index dc73de0c7..6bde827b9 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1513,10 +1513,12 @@ fn set_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), C Ok(()) } -fn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), ConfigError> { - let r = info.regs; - let kind = info.kind; - +fn find_and_set_brr( + r: stm32_metapac::usart::Usart, + kind: Kind, + kernel_clock: Hertz, + baudrate: u32, +) -> Result { #[cfg(not(usart_v4))] static DIVS: [(u16, ()); 1] = [(1, ())]; @@ -1548,12 +1550,8 @@ fn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result } }; - r.cr1().modify(|w| { - // disable uart - w.set_ue(false); - }); - let mut found_brr = None; + let mut over8 = false; for &(presc, _presc_val) in &DIVS { let brr = calculate_brr(baudrate, kernel_clock.0, presc as u32, mul); trace!( @@ -1564,106 +1562,6 @@ fn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result brr & 0x0F ); - if brr < brr_min { - #[cfg(not(usart_v1))] - if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { - r.brr().write_value(regs::Brr(((brr << 1) & !0xF) | (brr & 0x07))); - #[cfg(usart_v4)] - r.presc().write(|w| w.set_prescaler(_presc_val)); - found_brr = Some(brr); - break; - } - return Err(ConfigError::BaudrateTooHigh); - } - - if brr < brr_max { - r.brr().write_value(regs::Brr(brr)); - #[cfg(usart_v4)] - r.presc().write(|w| w.set_prescaler(_presc_val)); - found_brr = Some(brr); - break; - } - } - - let brr = found_brr.ok_or(ConfigError::BaudrateTooLow)?; - - trace!( - "Desired baudrate: {}, actual baudrate: {}", - baudrate, - kernel_clock.0 / brr * mul - ); - - r.cr1().modify(|w| { - // enable uart - w.set_ue(true); - }); - - Ok(()) -} - -fn configure( - info: &Info, - kernel_clock: Hertz, - config: &Config, - enable_rx: bool, - enable_tx: bool, -) -> Result<(), ConfigError> { - let r = info.regs; - let kind = info.kind; - - if !enable_rx && !enable_tx { - return Err(ConfigError::RxOrTxNotEnabled); - } - - #[cfg(not(usart_v4))] - static DIVS: [(u16, ()); 1] = [(1, ())]; - - #[cfg(usart_v4)] - static DIVS: [(u16, vals::Presc); 12] = [ - (1, vals::Presc::DIV1), - (2, vals::Presc::DIV2), - (4, vals::Presc::DIV4), - (6, vals::Presc::DIV6), - (8, vals::Presc::DIV8), - (10, vals::Presc::DIV10), - (12, vals::Presc::DIV12), - (16, vals::Presc::DIV16), - (32, vals::Presc::DIV32), - (64, vals::Presc::DIV64), - (128, vals::Presc::DIV128), - (256, vals::Presc::DIV256), - ]; - - let (mul, brr_min, brr_max) = match kind { - #[cfg(any(usart_v3, usart_v4))] - Kind::Lpuart => { - trace!("USART: Kind::Lpuart"); - (256, 0x300, 0x10_0000) - } - Kind::Uart => { - trace!("USART: Kind::Uart"); - (1, 0x10, 0x1_0000) - } - }; - - // UART must be disabled during configuration. - r.cr1().modify(|w| { - w.set_ue(false); - }); - - #[cfg(not(usart_v1))] - let mut over8 = false; - let mut found_brr = None; - for &(presc, _presc_val) in &DIVS { - let brr = calculate_brr(config.baudrate, kernel_clock.0, presc as u32, mul); - trace!( - "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", - presc, - brr, - brr >> 4, - brr & 0x0F - ); - if brr < brr_min { #[cfg(not(usart_v1))] if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { @@ -1686,18 +1584,63 @@ fn configure( } } - let brr = found_brr.ok_or(ConfigError::BaudrateTooLow)?; + match found_brr { + Some(brr) => { + #[cfg(not(usart_v1))] + let oversampling = if over8 { "8 bit" } else { "16 bit" }; + #[cfg(usart_v1)] + let oversampling = "default"; + trace!( + "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", + oversampling, + baudrate, + kernel_clock.0 / brr * mul + ); + Ok(over8) + } + None => Err(ConfigError::BaudrateTooLow), + } +} - #[cfg(not(usart_v1))] - let oversampling = if over8 { "8 bit" } else { "16 bit" }; - #[cfg(usart_v1)] - let oversampling = "default"; - trace!( - "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", - oversampling, - config.baudrate, - kernel_clock.0 / brr * mul - ); +fn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), ConfigError> { + let r = info.regs; + r.cr1().modify(|w| { + // disable uart + w.set_ue(false); + }); + let over8 = find_and_set_brr(r, info.kind, kernel_clock, baudrate)?; + + r.cr1().modify(|w| { + // enable uart + w.set_ue(true); + + #[cfg(not(usart_v1))] + w.set_over8(vals::Over8::from_bits(over8 as _)); + }); + + Ok(()) +} + +fn configure( + info: &Info, + kernel_clock: Hertz, + config: &Config, + enable_rx: bool, + enable_tx: bool, +) -> Result<(), ConfigError> { + let r = info.regs; + let kind = info.kind; + + if !enable_rx && !enable_tx { + return Err(ConfigError::RxOrTxNotEnabled); + } + + // UART must be disabled during configuration. + r.cr1().modify(|w| { + w.set_ue(false); + }); + + let over8 = find_and_set_brr(r, kind, kernel_clock, config.baudrate)?; r.cr2().write(|w| { w.set_stop(match config.stop_bits { From 32a1b232cb74e61edc98aa5f499e58a1b13d055b Mon Sep 17 00:00:00 2001 From: Enmanuel Parache Date: Thu, 7 Nov 2024 15:24:23 -0400 Subject: [PATCH 4/5] Fixed build error --- embassy-stm32/src/usart/mod.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 6bde827b9..2d801e6bf 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1513,12 +1513,7 @@ fn set_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), C Ok(()) } -fn find_and_set_brr( - r: stm32_metapac::usart::Usart, - kind: Kind, - kernel_clock: Hertz, - baudrate: u32, -) -> Result { +fn find_and_set_brr(r: Regs, kind: Kind, kernel_clock: Hertz, baudrate: u32) -> Result { #[cfg(not(usart_v4))] static DIVS: [(u16, ()); 1] = [(1, ())]; @@ -1551,7 +1546,11 @@ fn find_and_set_brr( }; let mut found_brr = None; + #[cfg(not(usart_v1))] let mut over8 = false; + #[cfg(usart_v1)] + let over8 = false; + for &(presc, _presc_val) in &DIVS { let brr = calculate_brr(baudrate, kernel_clock.0, presc as u32, mul); trace!( @@ -1608,7 +1607,11 @@ fn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result // disable uart w.set_ue(false); }); + + #[cfg(not(usart_v1))] let over8 = find_and_set_brr(r, info.kind, kernel_clock, baudrate)?; + #[cfg(usart_v1)] + let _over8 = find_and_set_brr(r, info.kind, kernel_clock, baudrate)?; r.cr1().modify(|w| { // enable uart @@ -1640,7 +1643,10 @@ fn configure( w.set_ue(false); }); + #[cfg(not(usart_v1))] let over8 = find_and_set_brr(r, kind, kernel_clock, config.baudrate)?; + #[cfg(usart_v1)] + let _over8 = find_and_set_brr(r, kind, kernel_clock, config.baudrate)?; r.cr2().write(|w| { w.set_stop(match config.stop_bits { From 8d2fbf0e143c99c0f10b204e5efbaa81749decbf Mon Sep 17 00:00:00 2001 From: Enmanuel Parache Date: Sat, 9 Nov 2024 12:38:02 -0400 Subject: [PATCH 5/5] Extend set_baudrate implementation to Buffered and RingBuffered structs --- embassy-stm32/src/usart/buffered.rs | 21 +++++++++++++++++++-- embassy-stm32/src/usart/ringbuffered.rs | 9 ++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index f7b2bf4b4..814be2858 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -12,8 +12,8 @@ use embassy_sync::waitqueue::AtomicWaker; #[cfg(not(any(usart_v1, usart_v2)))] use super::DePin; use super::{ - clear_interrupt_flags, configure, rdr, reconfigure, send_break, sr, tdr, Config, ConfigError, CtsPin, Error, Info, - Instance, Regs, RtsPin, RxPin, TxPin, + clear_interrupt_flags, configure, rdr, reconfigure, send_break, set_baudrate, sr, tdr, Config, ConfigError, CtsPin, + Error, Info, Instance, Regs, RtsPin, RxPin, TxPin, }; use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; use crate::interrupt::{self, InterruptExt}; @@ -441,6 +441,13 @@ impl<'d> BufferedUart<'d> { pub fn send_break(&self) { self.tx.send_break() } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + self.tx.set_baudrate(baudrate)?; + self.rx.set_baudrate(baudrate)?; + Ok(()) + } } impl<'d> BufferedUartRx<'d> { @@ -535,6 +542,11 @@ impl<'d> BufferedUartRx<'d> { Ok(()) } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + set_baudrate(self.info, self.kernel_clock, baudrate) + } } impl<'d> BufferedUartTx<'d> { @@ -625,6 +637,11 @@ impl<'d> BufferedUartTx<'d> { pub fn send_break(&self) { send_break(&self.info.regs); } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + set_baudrate(self.info, self.kernel_clock, baudrate) + } } impl<'d> Drop for BufferedUartRx<'d> { diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index a791ac2a9..560ce4e8f 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -8,7 +8,9 @@ use embassy_hal_internal::PeripheralRef; use embedded_io_async::ReadReady; use futures_util::future::{select, Either}; -use super::{clear_interrupt_flags, rdr, reconfigure, sr, Config, ConfigError, Error, Info, State, UartRx}; +use super::{ + clear_interrupt_flags, rdr, reconfigure, set_baudrate, sr, Config, ConfigError, Error, Info, State, UartRx, +}; use crate::dma::ReadableRingBuffer; use crate::gpio::{AnyPin, SealedPin as _}; use crate::mode::Async; @@ -213,6 +215,11 @@ impl<'d> RingBufferedUartRx<'d> { Either::Right(((), _)) => Ok(()), } } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + set_baudrate(self.info, self.kernel_clock, baudrate) + } } impl Drop for RingBufferedUartRx<'_> {