diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-12-02 23:52:20 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-12-02 23:52:20 +0000 |
| commit | 86b53a2ce3d93d07e5c8e4f4122e9588b87aafb7 (patch) | |
| tree | 679c0c1ddf37c55cede52b50d1adcc845fc22200 | |
| parent | f7a1f5bf562a5511f6c609bfbd32b03814b8d8f9 (diff) | |
| parent | 8d2fbf0e143c99c0f10b204e5efbaa81749decbf (diff) | |
Merge pull request #3512 from EnmanuelParache/stm32_usart_set_baudrate
stm32/usart: Changing baud rate
| -rw-r--r-- | embassy-stm32/src/usart/buffered.rs | 21 | ||||
| -rw-r--r-- | embassy-stm32/src/usart/mod.rs | 151 | ||||
| -rw-r--r-- | embassy-stm32/src/usart/ringbuffered.rs | 9 | ||||
| -rw-r--r-- | examples/stm32l1/src/bin/usart.rs | 37 |
4 files changed, 172 insertions, 46 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; | |||
| 12 | #[cfg(not(any(usart_v1, usart_v2)))] | 12 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 13 | use super::DePin; | 13 | use super::DePin; |
| 14 | use super::{ | 14 | use super::{ |
| 15 | clear_interrupt_flags, configure, rdr, reconfigure, send_break, sr, tdr, Config, ConfigError, CtsPin, Error, Info, | 15 | clear_interrupt_flags, configure, rdr, reconfigure, send_break, set_baudrate, sr, tdr, Config, ConfigError, CtsPin, |
| 16 | Instance, Regs, RtsPin, RxPin, TxPin, | 16 | Error, Info, Instance, Regs, RtsPin, RxPin, TxPin, |
| 17 | }; | 17 | }; |
| 18 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 18 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 19 | use crate::interrupt::{self, InterruptExt}; | 19 | use crate::interrupt::{self, InterruptExt}; |
| @@ -441,6 +441,13 @@ impl<'d> BufferedUart<'d> { | |||
| 441 | pub fn send_break(&self) { | 441 | pub fn send_break(&self) { |
| 442 | self.tx.send_break() | 442 | self.tx.send_break() |
| 443 | } | 443 | } |
| 444 | |||
| 445 | /// Set baudrate | ||
| 446 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 447 | self.tx.set_baudrate(baudrate)?; | ||
| 448 | self.rx.set_baudrate(baudrate)?; | ||
| 449 | Ok(()) | ||
| 450 | } | ||
| 444 | } | 451 | } |
| 445 | 452 | ||
| 446 | impl<'d> BufferedUartRx<'d> { | 453 | impl<'d> BufferedUartRx<'d> { |
| @@ -535,6 +542,11 @@ impl<'d> BufferedUartRx<'d> { | |||
| 535 | 542 | ||
| 536 | Ok(()) | 543 | Ok(()) |
| 537 | } | 544 | } |
| 545 | |||
| 546 | /// Set baudrate | ||
| 547 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 548 | set_baudrate(self.info, self.kernel_clock, baudrate) | ||
| 549 | } | ||
| 538 | } | 550 | } |
| 539 | 551 | ||
| 540 | impl<'d> BufferedUartTx<'d> { | 552 | impl<'d> BufferedUartTx<'d> { |
| @@ -625,6 +637,11 @@ impl<'d> BufferedUartTx<'d> { | |||
| 625 | pub fn send_break(&self) { | 637 | pub fn send_break(&self) { |
| 626 | send_break(&self.info.regs); | 638 | send_break(&self.info.regs); |
| 627 | } | 639 | } |
| 640 | |||
| 641 | /// Set baudrate | ||
| 642 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 643 | set_baudrate(self.info, self.kernel_clock, baudrate) | ||
| 644 | } | ||
| 628 | } | 645 | } |
| 629 | 646 | ||
| 630 | impl<'d> Drop for BufferedUartRx<'d> { | 647 | impl<'d> Drop for BufferedUartRx<'d> { |
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index d2fb85ee3..2d801e6bf 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs | |||
| @@ -539,6 +539,11 @@ impl<'d, M: Mode> UartTx<'d, M> { | |||
| 539 | pub fn send_break(&self) { | 539 | pub fn send_break(&self) { |
| 540 | send_break(&self.info.regs); | 540 | send_break(&self.info.regs); |
| 541 | } | 541 | } |
| 542 | |||
| 543 | /// Set baudrate | ||
| 544 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 545 | set_baudrate(self.info, self.kernel_clock, baudrate) | ||
| 546 | } | ||
| 542 | } | 547 | } |
| 543 | 548 | ||
| 544 | /// Wait until transmission complete | 549 | /// Wait until transmission complete |
| @@ -1014,6 +1019,11 @@ impl<'d, M: Mode> UartRx<'d, M> { | |||
| 1014 | } | 1019 | } |
| 1015 | Ok(()) | 1020 | Ok(()) |
| 1016 | } | 1021 | } |
| 1022 | |||
| 1023 | /// Set baudrate | ||
| 1024 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 1025 | set_baudrate(self.info, self.kernel_clock, baudrate) | ||
| 1026 | } | ||
| 1017 | } | 1027 | } |
| 1018 | 1028 | ||
| 1019 | impl<'d, M: Mode> Drop for UartTx<'d, M> { | 1029 | impl<'d, M: Mode> Drop for UartTx<'d, M> { |
| @@ -1455,6 +1465,13 @@ impl<'d, M: Mode> Uart<'d, M> { | |||
| 1455 | pub fn send_break(&self) { | 1465 | pub fn send_break(&self) { |
| 1456 | self.tx.send_break(); | 1466 | self.tx.send_break(); |
| 1457 | } | 1467 | } |
| 1468 | |||
| 1469 | /// Set baudrate | ||
| 1470 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 1471 | self.tx.set_baudrate(baudrate)?; | ||
| 1472 | self.rx.set_baudrate(baudrate)?; | ||
| 1473 | Ok(()) | ||
| 1474 | } | ||
| 1458 | } | 1475 | } |
| 1459 | 1476 | ||
| 1460 | fn reconfigure(info: &Info, kernel_clock: Hertz, config: &Config) -> Result<(), ConfigError> { | 1477 | 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<(), | |||
| 1470 | Ok(()) | 1487 | Ok(()) |
| 1471 | } | 1488 | } |
| 1472 | 1489 | ||
| 1473 | fn configure( | 1490 | fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 { |
| 1474 | info: &Info, | 1491 | // The calculation to be done to get the BRR is `mul * pclk / presc / baud` |
| 1475 | kernel_clock: Hertz, | 1492 | // To do this in 32-bit only we can't multiply `mul` and `pclk` |
| 1476 | config: &Config, | 1493 | let clock = pclk / presc; |
| 1477 | enable_rx: bool, | ||
| 1478 | enable_tx: bool, | ||
| 1479 | ) -> Result<(), ConfigError> { | ||
| 1480 | let r = info.regs; | ||
| 1481 | let kind = info.kind; | ||
| 1482 | 1494 | ||
| 1483 | if !enable_rx && !enable_tx { | 1495 | // The mul is applied as the last operation to prevent overflow |
| 1484 | return Err(ConfigError::RxOrTxNotEnabled); | 1496 | let brr = clock / baud * mul; |
| 1485 | } | 1497 | |
| 1498 | // The BRR calculation will be a bit off because of integer rounding. | ||
| 1499 | // Because we multiplied our inaccuracy with mul, our rounding now needs to be in proportion to mul. | ||
| 1500 | let rounding = ((clock % baud) * mul + (baud / 2)) / baud; | ||
| 1501 | |||
| 1502 | brr + rounding | ||
| 1503 | } | ||
| 1486 | 1504 | ||
| 1505 | fn set_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), ConfigError> { | ||
| 1506 | info.interrupt.disable(); | ||
| 1507 | |||
| 1508 | set_usart_baudrate(info, kernel_clock, baudrate)?; | ||
| 1509 | |||
| 1510 | info.interrupt.unpend(); | ||
| 1511 | unsafe { info.interrupt.enable() }; | ||
| 1512 | |||
| 1513 | Ok(()) | ||
| 1514 | } | ||
| 1515 | |||
| 1516 | fn find_and_set_brr(r: Regs, kind: Kind, kernel_clock: Hertz, baudrate: u32) -> Result<bool, ConfigError> { | ||
| 1487 | #[cfg(not(usart_v4))] | 1517 | #[cfg(not(usart_v4))] |
| 1488 | static DIVS: [(u16, ()); 1] = [(1, ())]; | 1518 | static DIVS: [(u16, ()); 1] = [(1, ())]; |
| 1489 | 1519 | ||
| @@ -1515,31 +1545,14 @@ fn configure( | |||
| 1515 | } | 1545 | } |
| 1516 | }; | 1546 | }; |
| 1517 | 1547 | ||
| 1518 | fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 { | 1548 | let mut found_brr = None; |
| 1519 | // The calculation to be done to get the BRR is `mul * pclk / presc / baud` | ||
| 1520 | // To do this in 32-bit only we can't multiply `mul` and `pclk` | ||
| 1521 | let clock = pclk / presc; | ||
| 1522 | |||
| 1523 | // The mul is applied as the last operation to prevent overflow | ||
| 1524 | let brr = clock / baud * mul; | ||
| 1525 | |||
| 1526 | // The BRR calculation will be a bit off because of integer rounding. | ||
| 1527 | // Because we multiplied our inaccuracy with mul, our rounding now needs to be in proportion to mul. | ||
| 1528 | let rounding = ((clock % baud) * mul + (baud / 2)) / baud; | ||
| 1529 | |||
| 1530 | brr + rounding | ||
| 1531 | } | ||
| 1532 | |||
| 1533 | // UART must be disabled during configuration. | ||
| 1534 | r.cr1().modify(|w| { | ||
| 1535 | w.set_ue(false); | ||
| 1536 | }); | ||
| 1537 | |||
| 1538 | #[cfg(not(usart_v1))] | 1549 | #[cfg(not(usart_v1))] |
| 1539 | let mut over8 = false; | 1550 | let mut over8 = false; |
| 1540 | let mut found_brr = None; | 1551 | #[cfg(usart_v1)] |
| 1552 | let over8 = false; | ||
| 1553 | |||
| 1541 | for &(presc, _presc_val) in &DIVS { | 1554 | for &(presc, _presc_val) in &DIVS { |
| 1542 | let brr = calculate_brr(config.baudrate, kernel_clock.0, presc as u32, mul); | 1555 | let brr = calculate_brr(baudrate, kernel_clock.0, presc as u32, mul); |
| 1543 | trace!( | 1556 | trace!( |
| 1544 | "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", | 1557 | "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", |
| 1545 | presc, | 1558 | presc, |
| @@ -1570,18 +1583,70 @@ fn configure( | |||
| 1570 | } | 1583 | } |
| 1571 | } | 1584 | } |
| 1572 | 1585 | ||
| 1573 | let brr = found_brr.ok_or(ConfigError::BaudrateTooLow)?; | 1586 | match found_brr { |
| 1587 | Some(brr) => { | ||
| 1588 | #[cfg(not(usart_v1))] | ||
| 1589 | let oversampling = if over8 { "8 bit" } else { "16 bit" }; | ||
| 1590 | #[cfg(usart_v1)] | ||
| 1591 | let oversampling = "default"; | ||
| 1592 | trace!( | ||
| 1593 | "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", | ||
| 1594 | oversampling, | ||
| 1595 | baudrate, | ||
| 1596 | kernel_clock.0 / brr * mul | ||
| 1597 | ); | ||
| 1598 | Ok(over8) | ||
| 1599 | } | ||
| 1600 | None => Err(ConfigError::BaudrateTooLow), | ||
| 1601 | } | ||
| 1602 | } | ||
| 1603 | |||
| 1604 | fn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), ConfigError> { | ||
| 1605 | let r = info.regs; | ||
| 1606 | r.cr1().modify(|w| { | ||
| 1607 | // disable uart | ||
| 1608 | w.set_ue(false); | ||
| 1609 | }); | ||
| 1610 | |||
| 1611 | #[cfg(not(usart_v1))] | ||
| 1612 | let over8 = find_and_set_brr(r, info.kind, kernel_clock, baudrate)?; | ||
| 1613 | #[cfg(usart_v1)] | ||
| 1614 | let _over8 = find_and_set_brr(r, info.kind, kernel_clock, baudrate)?; | ||
| 1615 | |||
| 1616 | r.cr1().modify(|w| { | ||
| 1617 | // enable uart | ||
| 1618 | w.set_ue(true); | ||
| 1619 | |||
| 1620 | #[cfg(not(usart_v1))] | ||
| 1621 | w.set_over8(vals::Over8::from_bits(over8 as _)); | ||
| 1622 | }); | ||
| 1623 | |||
| 1624 | Ok(()) | ||
| 1625 | } | ||
| 1626 | |||
| 1627 | fn configure( | ||
| 1628 | info: &Info, | ||
| 1629 | kernel_clock: Hertz, | ||
| 1630 | config: &Config, | ||
| 1631 | enable_rx: bool, | ||
| 1632 | enable_tx: bool, | ||
| 1633 | ) -> Result<(), ConfigError> { | ||
| 1634 | let r = info.regs; | ||
| 1635 | let kind = info.kind; | ||
| 1636 | |||
| 1637 | if !enable_rx && !enable_tx { | ||
| 1638 | return Err(ConfigError::RxOrTxNotEnabled); | ||
| 1639 | } | ||
| 1640 | |||
| 1641 | // UART must be disabled during configuration. | ||
| 1642 | r.cr1().modify(|w| { | ||
| 1643 | w.set_ue(false); | ||
| 1644 | }); | ||
| 1574 | 1645 | ||
| 1575 | #[cfg(not(usart_v1))] | 1646 | #[cfg(not(usart_v1))] |
| 1576 | let oversampling = if over8 { "8 bit" } else { "16 bit" }; | 1647 | let over8 = find_and_set_brr(r, kind, kernel_clock, config.baudrate)?; |
| 1577 | #[cfg(usart_v1)] | 1648 | #[cfg(usart_v1)] |
| 1578 | let oversampling = "default"; | 1649 | let _over8 = find_and_set_brr(r, kind, kernel_clock, config.baudrate)?; |
| 1579 | trace!( | ||
| 1580 | "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", | ||
| 1581 | oversampling, | ||
| 1582 | config.baudrate, | ||
| 1583 | kernel_clock.0 / brr * mul | ||
| 1584 | ); | ||
| 1585 | 1650 | ||
| 1586 | r.cr2().write(|w| { | 1651 | r.cr2().write(|w| { |
| 1587 | w.set_stop(match config.stop_bits { | 1652 | w.set_stop(match config.stop_bits { |
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; | |||
| 8 | use embedded_io_async::ReadReady; | 8 | use embedded_io_async::ReadReady; |
| 9 | use futures_util::future::{select, Either}; | 9 | use futures_util::future::{select, Either}; |
| 10 | 10 | ||
| 11 | use super::{clear_interrupt_flags, rdr, reconfigure, sr, Config, ConfigError, Error, Info, State, UartRx}; | 11 | use super::{ |
| 12 | clear_interrupt_flags, rdr, reconfigure, set_baudrate, sr, Config, ConfigError, Error, Info, State, UartRx, | ||
| 13 | }; | ||
| 12 | use crate::dma::ReadableRingBuffer; | 14 | use crate::dma::ReadableRingBuffer; |
| 13 | use crate::gpio::{AnyPin, SealedPin as _}; | 15 | use crate::gpio::{AnyPin, SealedPin as _}; |
| 14 | use crate::mode::Async; | 16 | use crate::mode::Async; |
| @@ -213,6 +215,11 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 213 | Either::Right(((), _)) => Ok(()), | 215 | Either::Right(((), _)) => Ok(()), |
| 214 | } | 216 | } |
| 215 | } | 217 | } |
| 218 | |||
| 219 | /// Set baudrate | ||
| 220 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 221 | set_baudrate(self.info, self.kernel_clock, baudrate) | ||
| 222 | } | ||
| 216 | } | 223 | } |
| 217 | 224 | ||
| 218 | impl Drop for RingBufferedUartRx<'_> { | 225 | impl Drop for RingBufferedUartRx<'_> { |
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 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use cortex_m_rt::entry; | ||
| 5 | use defmt::*; | ||
| 6 | use embassy_stm32::usart::{Config, Uart}; | ||
| 7 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | bind_interrupts!(struct Irqs { | ||
| 11 | USART2 => usart::InterruptHandler<peripherals::USART2>; | ||
| 12 | }); | ||
| 13 | |||
| 14 | #[entry] | ||
| 15 | fn main() -> ! { | ||
| 16 | info!("Hello World!"); | ||
| 17 | |||
| 18 | let p = embassy_stm32::init(Default::default()); | ||
| 19 | |||
| 20 | let config = Config::default(); | ||
| 21 | let mut usart = Uart::new_blocking(p.USART2, p.PA3, p.PA2, config).unwrap(); | ||
| 22 | let desired_baudrate = 9600; // Default is 115200 and 9600 is used as example | ||
| 23 | |||
| 24 | match usart.set_baudrate(desired_baudrate) { | ||
| 25 | Ok(_) => info!("Baud rate set to {}", desired_baudrate), | ||
| 26 | Err(err) => error!("Error setting baudrate to {}: {}", desired_baudrate, err), | ||
| 27 | } | ||
| 28 | |||
| 29 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); | ||
| 30 | info!("wrote Hello, starting echo"); | ||
| 31 | |||
| 32 | let mut buf = [0u8; 1]; | ||
| 33 | loop { | ||
| 34 | unwrap!(usart.blocking_read(&mut buf)); | ||
| 35 | unwrap!(usart.blocking_write(&buf)); | ||
| 36 | } | ||
| 37 | } | ||
