Merge pull request #3512 from EnmanuelParache/stm32_usart_set_baudrate

stm32/usart: Changing baud rate
This commit is contained in:
Dario Nieuwenhuis 2024-12-02 23:52:20 +00:00 committed by GitHub
commit 86b53a2ce3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 172 additions and 46 deletions

View File

@ -12,8 +12,8 @@ use embassy_sync::waitqueue::AtomicWaker;
#[cfg(not(any(usart_v1, usart_v2)))] #[cfg(not(any(usart_v1, usart_v2)))]
use super::DePin; use super::DePin;
use super::{ use super::{
clear_interrupt_flags, configure, rdr, reconfigure, send_break, sr, tdr, Config, ConfigError, CtsPin, Error, Info, clear_interrupt_flags, configure, rdr, reconfigure, send_break, set_baudrate, sr, tdr, Config, ConfigError, CtsPin,
Instance, Regs, RtsPin, RxPin, TxPin, Error, Info, Instance, Regs, RtsPin, RxPin, TxPin,
}; };
use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
use crate::interrupt::{self, InterruptExt}; use crate::interrupt::{self, InterruptExt};
@ -441,6 +441,13 @@ impl<'d> BufferedUart<'d> {
pub fn send_break(&self) { pub fn send_break(&self) {
self.tx.send_break() 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> { impl<'d> BufferedUartRx<'d> {
@ -535,6 +542,11 @@ impl<'d> BufferedUartRx<'d> {
Ok(()) Ok(())
} }
/// Set baudrate
pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {
set_baudrate(self.info, self.kernel_clock, baudrate)
}
} }
impl<'d> BufferedUartTx<'d> { impl<'d> BufferedUartTx<'d> {
@ -625,6 +637,11 @@ impl<'d> BufferedUartTx<'d> {
pub fn send_break(&self) { pub fn send_break(&self) {
send_break(&self.info.regs); 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> { impl<'d> Drop for BufferedUartRx<'d> {

View File

@ -539,6 +539,11 @@ impl<'d, M: Mode> UartTx<'d, M> {
pub fn send_break(&self) { pub fn send_break(&self) {
send_break(&self.info.regs); 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 /// Wait until transmission complete
@ -1014,6 +1019,11 @@ impl<'d, M: Mode> UartRx<'d, M> {
} }
Ok(()) 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> { impl<'d, M: Mode> Drop for UartTx<'d, M> {
@ -1455,6 +1465,13 @@ impl<'d, M: Mode> Uart<'d, M> {
pub fn send_break(&self) { pub fn send_break(&self) {
self.tx.send_break(); 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> { fn reconfigure(info: &Info, kernel_clock: Hertz, config: &Config) -> Result<(), ConfigError> {
@ -1470,20 +1487,33 @@ fn reconfigure(info: &Info, kernel_clock: Hertz, config: &Config) -> Result<(),
Ok(()) Ok(())
} }
fn configure( fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 {
info: &Info, // The calculation to be done to get the BRR is `mul * pclk / presc / baud`
kernel_clock: Hertz, // To do this in 32-bit only we can't multiply `mul` and `pclk`
config: &Config, let clock = pclk / presc;
enable_rx: bool,
enable_tx: bool,
) -> Result<(), ConfigError> {
let r = info.regs;
let kind = info.kind;
if !enable_rx && !enable_tx { // The mul is applied as the last operation to prevent overflow
return Err(ConfigError::RxOrTxNotEnabled); 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 find_and_set_brr(r: Regs, kind: Kind, kernel_clock: Hertz, baudrate: u32) -> Result<bool, ConfigError> {
#[cfg(not(usart_v4))] #[cfg(not(usart_v4))]
static DIVS: [(u16, ()); 1] = [(1, ())]; static DIVS: [(u16, ()); 1] = [(1, ())];
@ -1515,31 +1545,14 @@ fn configure(
} }
}; };
fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 { let mut found_brr = None;
// 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);
});
#[cfg(not(usart_v1))] #[cfg(not(usart_v1))]
let mut over8 = false; let mut over8 = false;
let mut found_brr = None; #[cfg(usart_v1)]
let over8 = false;
for &(presc, _presc_val) in &DIVS { for &(presc, _presc_val) in &DIVS {
let brr = calculate_brr(config.baudrate, kernel_clock.0, presc as u32, mul); let brr = calculate_brr(baudrate, kernel_clock.0, presc as u32, mul);
trace!( trace!(
"USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})",
presc, presc,
@ -1570,18 +1583,70 @@ 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),
}
}
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);
});
#[cfg(not(usart_v1))] #[cfg(not(usart_v1))]
let oversampling = if over8 { "8 bit" } else { "16 bit" }; let over8 = find_and_set_brr(r, info.kind, kernel_clock, baudrate)?;
#[cfg(usart_v1)] #[cfg(usart_v1)]
let oversampling = "default"; let _over8 = find_and_set_brr(r, info.kind, kernel_clock, baudrate)?;
trace!(
"Using {} oversampling, desired baudrate: {}, actual baudrate: {}", r.cr1().modify(|w| {
oversampling, // enable uart
config.baudrate, w.set_ue(true);
kernel_clock.0 / brr * mul
); #[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);
});
#[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| { r.cr2().write(|w| {
w.set_stop(match config.stop_bits { w.set_stop(match config.stop_bits {

View File

@ -8,7 +8,9 @@ use embassy_hal_internal::PeripheralRef;
use embedded_io_async::ReadReady; use embedded_io_async::ReadReady;
use futures_util::future::{select, Either}; 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::dma::ReadableRingBuffer;
use crate::gpio::{AnyPin, SealedPin as _}; use crate::gpio::{AnyPin, SealedPin as _};
use crate::mode::Async; use crate::mode::Async;
@ -213,6 +215,11 @@ impl<'d> RingBufferedUartRx<'d> {
Either::Right(((), _)) => Ok(()), 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<'_> { impl Drop for RingBufferedUartRx<'_> {

View File

@ -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<peripherals::USART2>;
});
#[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));
}
}