diff --git a/Cargo.toml b/Cargo.toml index 7c30fff7..237e3910 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ cortex-m = "0.7.4" cortex-m-rt = "0.7.1" nb = "1" rand_core = "0.6.3" -stm32f4 = "0.15.1" +stm32f4 = { git = "https://github.com/stm32-rs/stm32-rs-nightlies" } synopsys-usb-otg = { version = "0.3.0", features = ["cortex-m"], optional = true } sdio-host = { version = "0.6.0", optional = true } embedded-dma = "0.2.0" diff --git a/src/lib.rs b/src/lib.rs index 8b9b2f1d..ef12a548 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -172,6 +172,9 @@ pub mod time; #[cfg(feature = "device-selected")] pub mod timer; #[cfg(feature = "device-selected")] +#[cfg(feature = "uart4")] +pub mod uart; +#[cfg(feature = "device-selected")] pub mod watchdog; #[cfg(feature = "device-selected")] diff --git a/src/prelude.rs b/src/prelude.rs index a04b45ec..f393c237 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -79,3 +79,5 @@ pub use crate::timer::PwmExt as _stm32f4xx_hal_timer_PwmExt; pub use crate::timer::SysMonoTimerExt as _stm32f4xx_hal_timer_SysMonoTimerExt; pub use crate::timer::SysTimerExt as _stm32f4xx_hal_timer_SysCounterExt; pub use crate::timer::TimerExt as _stm32f4xx_hal_timer_TimerExt; +#[cfg(feature = "uart4")] +pub use crate::uart::SerialExt as _stm32f4xx_hal_uart_SerialExt; diff --git a/src/serial.rs b/src/serial.rs index 9ad4714c..c3bccd30 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -1,9 +1,8 @@ -//! -//! Asynchronous serial communication using UART/USART peripherals +//! Asynchronous serial communication using USART peripherals //! //! # Word length //! -//! By default, the UART/USART uses 8 data bits. The `Serial`, `Rx`, and `Tx` structs implement +//! By default, the USART uses 8 data bits. The `Serial`, `Rx`, and `Tx` structs implement //! the embedded-hal read and write traits with `u8` as the word type. //! //! You can also configure the hardware to use 9 data bits with the `Config` `wordlength_9()` @@ -25,6 +24,7 @@ use nb::block; mod hal_02; mod hal_1; +mod uart_impls; use crate::gpio::{Const, PinA, PushPull, SetAlternate}; @@ -236,50 +236,6 @@ impl Rx { _word: PhantomData, } } - - /// Start listening for an rx not empty interrupt event - /// - /// Note, you will also have to enable the corresponding interrupt - /// in the NVIC to start receiving events. - pub fn listen(&mut self) { - unsafe { (*USART::ptr()).cr1.modify(|_, w| w.rxneie().set_bit()) } - } - - /// Stop listening for the rx not empty interrupt event - pub fn unlisten(&mut self) { - unsafe { (*USART::ptr()).cr1.modify(|_, w| w.rxneie().clear_bit()) } - } - - /// Start listening for a line idle interrupt event - /// - /// Note, you will also have to enable the corresponding interrupt - /// in the NVIC to start receiving events. - pub fn listen_idle(&mut self) { - unsafe { (*USART::ptr()).cr1.modify(|_, w| w.idleie().set_bit()) } - } - - /// Stop listening for the line idle interrupt event - pub fn unlisten_idle(&mut self) { - unsafe { (*USART::ptr()).cr1.modify(|_, w| w.idleie().clear_bit()) } - } - - /// Return true if the line idle status is set - pub fn is_idle(&self) -> bool { - unsafe { (*USART::ptr()).sr.read().idle().bit_is_set() } - } - - /// Return true if the rx register is not empty (and can be read) - pub fn is_rx_not_empty(&self) -> bool { - unsafe { (*USART::ptr()).sr.read().rxne().bit_is_set() } - } - - /// Clear idle line interrupt flag - pub fn clear_idle_interrupt(&self) { - unsafe { - let _ = (*USART::ptr()).sr.read(); - let _ = (*USART::ptr()).dr.read(); - } - } } impl Tx { @@ -289,24 +245,6 @@ impl Tx { _word: PhantomData, } } - - /// Start listening for a tx empty interrupt event - /// - /// Note, you will also have to enable the corresponding interrupt - /// in the NVIC to start receiving events. - pub fn listen(&mut self) { - unsafe { (*USART::ptr()).cr1.modify(|_, w| w.txeie().set_bit()) } - } - - /// Stop listening for the tx empty interrupt event - pub fn unlisten(&mut self) { - unsafe { (*USART::ptr()).cr1.modify(|_, w| w.txeie().clear_bit()) } - } - - /// Return true if the tx register is empty (and can accept data) - pub fn is_tx_empty(&self) -> bool { - unsafe { (*USART::ptr()).sr.read().txe().bit_is_set() } - } } impl AsRef> for Serial { @@ -692,50 +630,6 @@ where } impl Serial { - /// Starts listening for an interrupt event - /// - /// Note, you will also have to enable the corresponding interrupt - /// in the NVIC to start receiving events. - pub fn listen(&mut self, event: Event) { - match event { - Event::Rxne => unsafe { (*USART::ptr()).cr1.modify(|_, w| w.rxneie().set_bit()) }, - Event::Txe => unsafe { (*USART::ptr()).cr1.modify(|_, w| w.txeie().set_bit()) }, - Event::Idle => unsafe { (*USART::ptr()).cr1.modify(|_, w| w.idleie().set_bit()) }, - } - } - - /// Stop listening for an interrupt event - pub fn unlisten(&mut self, event: Event) { - match event { - Event::Rxne => unsafe { (*USART::ptr()).cr1.modify(|_, w| w.rxneie().clear_bit()) }, - Event::Txe => unsafe { (*USART::ptr()).cr1.modify(|_, w| w.txeie().clear_bit()) }, - Event::Idle => unsafe { (*USART::ptr()).cr1.modify(|_, w| w.idleie().clear_bit()) }, - } - } - - /// Return true if the line idle status is set - pub fn is_idle(&self) -> bool { - unsafe { (*USART::ptr()).sr.read().idle().bit_is_set() } - } - - /// Return true if the tx register is empty (and can accept data) - pub fn is_tx_empty(&self) -> bool { - unsafe { (*USART::ptr()).sr.read().txe().bit_is_set() } - } - - /// Return true if the rx register is not empty (and can be read) - pub fn is_rx_not_empty(&self) -> bool { - unsafe { (*USART::ptr()).sr.read().rxne().bit_is_set() } - } - - /// Clear idle line interrupt flag - pub fn clear_idle_interrupt(&self) { - unsafe { - let _ = (*USART::ptr()).sr.read(); - let _ = (*USART::ptr()).dr.read(); - } - } - pub fn split(self) -> (Tx, Rx) { (self.tx, self.rx) } @@ -795,47 +689,31 @@ unsafe impl PeriAddress for Tx { impl Serial { fn config_stop(self, config: config::Config) -> Self { - self.usart.set_stopbits(config.stopbits); + self.set_stopbits(config.stopbits); self } + + fn set_stopbits(&self, bits: config::StopBits) { + use crate::pac::usart1::cr2::STOP_A; + use config::StopBits; + + unsafe { &(*USART::ptr()) }.cr2.write(|w| { + w.stop().variant(match bits { + StopBits::STOP0P5 => STOP_A::Stop0p5, + StopBits::STOP1 => STOP_A::Stop1, + StopBits::STOP1P5 => STOP_A::Stop1p5, + StopBits::STOP2 => STOP_A::Stop2, + }) + }); + } } -#[cfg(any( - feature = "stm32f405", - feature = "stm32f407", - feature = "stm32f415", - feature = "stm32f417", - feature = "stm32f427", - feature = "stm32f429", - feature = "stm32f437", - feature = "stm32f439", - feature = "stm32f446", - feature = "stm32f469", - feature = "stm32f479" -))] -use crate::pac::uart4 as uart_base; - -#[cfg(not(any( - feature = "stm32f405", - feature = "stm32f407", - feature = "stm32f415", - feature = "stm32f417", - feature = "stm32f427", - feature = "stm32f429", - feature = "stm32f437", - feature = "stm32f439", - feature = "stm32f446", - feature = "stm32f469", - feature = "stm32f479" -)))] use crate::pac::usart1 as uart_base; // Implemented by all USART instances pub trait Instance: crate::Sealed + rcc::Enable + rcc::Reset + rcc::BusClock { #[doc(hidden)] fn ptr() -> *const uart_base::RegisterBlock; - #[doc(hidden)] - fn set_stopbits(&self, bits: config::StopBits); } macro_rules! halUsart { @@ -848,58 +726,6 @@ macro_rules! halUsart { fn ptr() -> *const uart_base::RegisterBlock { <$USART>::ptr() as *const _ } - - fn set_stopbits(&self, bits: config::StopBits) { - use crate::pac::usart1::cr2::STOP_A; - use config::StopBits; - - self.cr2.write(|w| { - w.stop().variant(match bits { - StopBits::STOP0P5 => STOP_A::Stop0p5, - StopBits::STOP1 => STOP_A::Stop1, - StopBits::STOP1P5 => STOP_A::Stop1p5, - StopBits::STOP2 => STOP_A::Stop2, - }) - }); - } - } - }; -} - -// TODO: fix stm32f413 UARTs -#[cfg(any( - feature = "uart4", - feature = "uart5", - feature = "uart7", - feature = "uart8", - feature = "uart9", - feature = "uart10" -))] -#[cfg(not(any(feature = "stm32f413", feature = "stm32f423",)))] -macro_rules! halUart { - ($USART:ty, $Serial:ident, $Tx:ident, $Rx:ident) => { - pub type $Serial = Serial<$USART, PINS, WORD>; - pub type $Tx = Tx<$USART, WORD>; - pub type $Rx = Rx<$USART, WORD>; - - impl Instance for $USART { - fn ptr() -> *const uart_base::RegisterBlock { - <$USART>::ptr() as *const _ - } - - fn set_stopbits(&self, bits: config::StopBits) { - use crate::pac::uart4::cr2::STOP_A; - use config::StopBits; - - self.cr2.write(|w| { - w.stop().variant(match bits { - StopBits::STOP0P5 => STOP_A::Stop1, - StopBits::STOP1 => STOP_A::Stop1, - StopBits::STOP1P5 => STOP_A::Stop2, - StopBits::STOP2 => STOP_A::Stop2, - }) - }); - } } }; } @@ -910,138 +736,3 @@ halUsart! { pac::USART6, Serial6, Rx6, Tx6 } #[cfg(feature = "usart3")] halUsart! { pac::USART3, Serial3, Rx3, Tx3 } - -#[cfg(feature = "uart4")] -#[cfg(not(any(feature = "stm32f413", feature = "stm32f423")))] -halUart! { pac::UART4, Serial4, Rx4, Tx4 } -#[cfg(feature = "uart5")] -#[cfg(not(any(feature = "stm32f413", feature = "stm32f423")))] -halUart! { pac::UART5, Serial5, Rx5, Tx5 } - -//#[cfg(feature = "uart4")] -//#[cfg(any(feature = "stm32f413", feature = "stm32f423"))] -//halUsart! { pac::UART4, Serial4, Rx4, Tx4 } -#[cfg(feature = "uart5")] -#[cfg(any(feature = "stm32f413", feature = "stm32f423"))] -halUsart! { pac::UART5, Serial5, Rx5, Tx5 } - -#[cfg(feature = "uart7")] -halUsart! { pac::UART7, Serial7, Rx7, Tx7 } -#[cfg(feature = "uart8")] -halUsart! { pac::UART8, Serial8, Rx8, Tx8 } -#[cfg(feature = "uart9")] -halUsart! { pac::UART9, Serial9, Rx9, Tx9 } -#[cfg(feature = "uart10")] -halUsart! { pac::UART10, Serial10, Rx10, Tx10 } - -impl fmt::Write for Serial { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.tx.write_str(s) - } -} - -impl fmt::Write for Tx { - fn write_str(&mut self, s: &str) -> fmt::Result { - s.bytes() - .try_for_each(|c| block!(self.write(c))) - .map_err(|_| fmt::Error) - } -} - -impl Rx { - fn read(&mut self) -> nb::Result { - // Delegate to the Read implementation, then truncate to 8 bits - Rx::::new().read().map(|word16| word16 as u8) - } -} - -impl Rx { - fn read(&mut self) -> nb::Result { - // NOTE(unsafe) atomic read with no side effects - let sr = unsafe { (*USART::ptr()).sr.read() }; - - // Any error requires the dr to be read to clear - if sr.pe().bit_is_set() - || sr.fe().bit_is_set() - || sr.nf().bit_is_set() - || sr.ore().bit_is_set() - { - unsafe { (*USART::ptr()).dr.read() }; - } - - Err(if sr.pe().bit_is_set() { - Error::Parity.into() - } else if sr.fe().bit_is_set() { - Error::FrameFormat.into() - } else if sr.nf().bit_is_set() { - Error::Noise.into() - } else if sr.ore().bit_is_set() { - Error::Overrun.into() - } else if sr.rxne().bit_is_set() { - // NOTE(unsafe) atomic read from stateless register - return Ok(unsafe { &*USART::ptr() }.dr.read().dr().bits()); - } else { - nb::Error::WouldBlock - }) - } -} - -impl Tx { - fn write(&mut self, word: u8) -> nb::Result<(), Error> { - // Delegate to u16 version - Tx::::new().write(u16::from(word)) - } - - fn flush(&mut self) -> nb::Result<(), Error> { - // Delegate to u16 version - Tx::::new().flush() - } - - fn bwrite_all(&mut self, bytes: &[u8]) -> Result<(), Error> { - for &b in bytes { - nb::block!(self.write(b))?; - } - Ok(()) - } - - fn bflush(&mut self) -> Result<(), Error> { - nb::block!(self.flush()) - } -} - -impl Tx { - fn write(&mut self, word: u16) -> nb::Result<(), Error> { - // NOTE(unsafe) atomic read with no side effects - let sr = unsafe { (*USART::ptr()).sr.read() }; - - if sr.txe().bit_is_set() { - // NOTE(unsafe) atomic write to stateless register - unsafe { &*USART::ptr() }.dr.write(|w| w.dr().bits(word)); - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } - } - - fn flush(&mut self) -> nb::Result<(), Error> { - // NOTE(unsafe) atomic read with no side effects - let sr = unsafe { (*USART::ptr()).sr.read() }; - - if sr.tc().bit_is_set() { - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } - } - - fn bwrite_all(&mut self, buffer: &[u16]) -> Result<(), Error> { - for &b in buffer { - nb::block!(self.write(b))?; - } - Ok(()) - } - - fn bflush(&mut self) -> Result<(), Error> { - nb::block!(self.flush()) - } -} diff --git a/src/serial/uart_impls.rs b/src/serial/uart_impls.rs new file mode 100644 index 00000000..695db3a4 --- /dev/null +++ b/src/serial/uart_impls.rs @@ -0,0 +1,225 @@ +use super::*; + +impl Rx { + /// Start listening for an rx not empty interrupt event + /// + /// Note, you will also have to enable the corresponding interrupt + /// in the NVIC to start receiving events. + pub fn listen(&mut self) { + unsafe { (*USART::ptr()).cr1.modify(|_, w| w.rxneie().set_bit()) } + } + + /// Stop listening for the rx not empty interrupt event + pub fn unlisten(&mut self) { + unsafe { (*USART::ptr()).cr1.modify(|_, w| w.rxneie().clear_bit()) } + } + + /// Start listening for a line idle interrupt event + /// + /// Note, you will also have to enable the corresponding interrupt + /// in the NVIC to start receiving events. + pub fn listen_idle(&mut self) { + unsafe { (*USART::ptr()).cr1.modify(|_, w| w.idleie().set_bit()) } + } + + /// Stop listening for the line idle interrupt event + pub fn unlisten_idle(&mut self) { + unsafe { (*USART::ptr()).cr1.modify(|_, w| w.idleie().clear_bit()) } + } + + /// Return true if the line idle status is set + pub fn is_idle(&self) -> bool { + unsafe { (*USART::ptr()).sr.read().idle().bit_is_set() } + } + + /// Return true if the rx register is not empty (and can be read) + pub fn is_rx_not_empty(&self) -> bool { + unsafe { (*USART::ptr()).sr.read().rxne().bit_is_set() } + } + + /// Clear idle line interrupt flag + pub fn clear_idle_interrupt(&self) { + unsafe { + let _ = (*USART::ptr()).sr.read(); + let _ = (*USART::ptr()).dr.read(); + } + } +} + +impl Tx { + /// Start listening for a tx empty interrupt event + /// + /// Note, you will also have to enable the corresponding interrupt + /// in the NVIC to start receiving events. + pub fn listen(&mut self) { + unsafe { (*USART::ptr()).cr1.modify(|_, w| w.txeie().set_bit()) } + } + + /// Stop listening for the tx empty interrupt event + pub fn unlisten(&mut self) { + unsafe { (*USART::ptr()).cr1.modify(|_, w| w.txeie().clear_bit()) } + } + + /// Return true if the tx register is empty (and can accept data) + pub fn is_tx_empty(&self) -> bool { + unsafe { (*USART::ptr()).sr.read().txe().bit_is_set() } + } +} + +impl Serial { + /// Starts listening for an interrupt event + /// + /// Note, you will also have to enable the corresponding interrupt + /// in the NVIC to start receiving events. + pub fn listen(&mut self, event: Event) { + match event { + Event::Rxne => unsafe { (*USART::ptr()).cr1.modify(|_, w| w.rxneie().set_bit()) }, + Event::Txe => unsafe { (*USART::ptr()).cr1.modify(|_, w| w.txeie().set_bit()) }, + Event::Idle => unsafe { (*USART::ptr()).cr1.modify(|_, w| w.idleie().set_bit()) }, + } + } + + /// Stop listening for an interrupt event + pub fn unlisten(&mut self, event: Event) { + match event { + Event::Rxne => unsafe { (*USART::ptr()).cr1.modify(|_, w| w.rxneie().clear_bit()) }, + Event::Txe => unsafe { (*USART::ptr()).cr1.modify(|_, w| w.txeie().clear_bit()) }, + Event::Idle => unsafe { (*USART::ptr()).cr1.modify(|_, w| w.idleie().clear_bit()) }, + } + } + + /// Return true if the line idle status is set + pub fn is_idle(&self) -> bool { + unsafe { (*USART::ptr()).sr.read().idle().bit_is_set() } + } + + /// Return true if the tx register is empty (and can accept data) + pub fn is_tx_empty(&self) -> bool { + unsafe { (*USART::ptr()).sr.read().txe().bit_is_set() } + } + + /// Return true if the rx register is not empty (and can be read) + pub fn is_rx_not_empty(&self) -> bool { + unsafe { (*USART::ptr()).sr.read().rxne().bit_is_set() } + } + + /// Clear idle line interrupt flag + pub fn clear_idle_interrupt(&self) { + unsafe { + let _ = (*USART::ptr()).sr.read(); + let _ = (*USART::ptr()).dr.read(); + } + } +} + +impl fmt::Write for Serial { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.tx.write_str(s) + } +} + +impl fmt::Write for Tx { + fn write_str(&mut self, s: &str) -> fmt::Result { + s.bytes() + .try_for_each(|c| block!(self.write(c))) + .map_err(|_| fmt::Error) + } +} + +impl Rx { + pub(super) fn read(&mut self) -> nb::Result { + // Delegate to the Read implementation, then truncate to 8 bits + Rx::::new().read().map(|word16| word16 as u8) + } +} + +impl Rx { + pub(super) fn read(&mut self) -> nb::Result { + // NOTE(unsafe) atomic read with no side effects + let sr = unsafe { (*USART::ptr()).sr.read() }; + + // Any error requires the dr to be read to clear + if sr.pe().bit_is_set() + || sr.fe().bit_is_set() + || sr.nf().bit_is_set() + || sr.ore().bit_is_set() + { + unsafe { (*USART::ptr()).dr.read() }; + } + + Err(if sr.pe().bit_is_set() { + Error::Parity.into() + } else if sr.fe().bit_is_set() { + Error::FrameFormat.into() + } else if sr.nf().bit_is_set() { + Error::Noise.into() + } else if sr.ore().bit_is_set() { + Error::Overrun.into() + } else if sr.rxne().bit_is_set() { + // NOTE(unsafe) atomic read from stateless register + return Ok(unsafe { &*USART::ptr() }.dr.read().dr().bits()); + } else { + nb::Error::WouldBlock + }) + } +} + +impl Tx { + pub(super) fn write(&mut self, word: u8) -> nb::Result<(), Error> { + // Delegate to u16 version + Tx::::new().write(u16::from(word)) + } + + pub(super) fn flush(&mut self) -> nb::Result<(), Error> { + // Delegate to u16 version + Tx::::new().flush() + } + + pub(super) fn bwrite_all(&mut self, bytes: &[u8]) -> Result<(), Error> { + for &b in bytes { + nb::block!(self.write(b))?; + } + Ok(()) + } + + pub(super) fn bflush(&mut self) -> Result<(), Error> { + nb::block!(self.flush()) + } +} + +impl Tx { + pub(super) fn write(&mut self, word: u16) -> nb::Result<(), Error> { + // NOTE(unsafe) atomic read with no side effects + let sr = unsafe { (*USART::ptr()).sr.read() }; + + if sr.txe().bit_is_set() { + // NOTE(unsafe) atomic write to stateless register + unsafe { &*USART::ptr() }.dr.write(|w| w.dr().bits(word)); + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } + + pub(super) fn flush(&mut self) -> nb::Result<(), Error> { + // NOTE(unsafe) atomic read with no side effects + let sr = unsafe { (*USART::ptr()).sr.read() }; + + if sr.tc().bit_is_set() { + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } + + pub(super) fn bwrite_all(&mut self, buffer: &[u16]) -> Result<(), Error> { + for &b in buffer { + nb::block!(self.write(b))?; + } + Ok(()) + } + + pub(super) fn bflush(&mut self) -> Result<(), Error> { + nb::block!(self.flush()) + } +} diff --git a/src/uart.rs b/src/uart.rs new file mode 100644 index 00000000..c4da100c --- /dev/null +++ b/src/uart.rs @@ -0,0 +1,576 @@ +//! Asynchronous serial communication using UART peripherals +//! +//! # Word length +//! +//! By default, the UART uses 8 data bits. The `Serial`, `Rx`, and `Tx` structs implement +//! the embedded-hal read and write traits with `u8` as the word type. +//! +//! You can also configure the hardware to use 9 data bits with the `Config` `wordlength_9()` +//! function. After creating a `Serial` with this option, use the `with_u16_data()` function to +//! convert the `Serial<_, _, u8>` object into a `Serial<_, _, u16>` that can send and receive +//! `u16`s. +//! +//! In this mode, the `Serial<_, _, u16>`, `Rx<_, u16>`, and `Tx<_, u16>` structs instead implement +//! the embedded-hal read and write traits with `u16` as the word type. You can use these +//! implementations for 9-bit words. +//! + +use core::fmt; +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; + +use crate::rcc; +use nb::block; + +#[path = "./serial/hal_02.rs"] +mod hal_02; +#[path = "./serial/hal_1.rs"] +mod hal_1; +#[path = "./serial/uart_impls.rs"] +mod uart_impls; + +use crate::pac::{self, RCC}; + +use crate::gpio::NoPin; +use crate::rcc::Clocks; + +use crate::dma::traits::PeriAddress; + +pub use crate::serial::{config, Event, NoRx, NoTx, Pins, RxPin, TxPin}; +pub use config::Config; +/// Serial error +pub use embedded_hal_one::serial::ErrorKind as Error; + +/// Serial abstraction +pub struct Serial { + usart: USART, + pins: PINS, + tx: Tx, + rx: Rx, +} + +/// Serial receiver +pub struct Rx { + _usart: PhantomData, + _word: PhantomData, +} + +/// Serial transmitter +pub struct Tx { + _usart: PhantomData, + _word: PhantomData, +} + +impl Rx { + fn new() -> Self { + Self { + _usart: PhantomData, + _word: PhantomData, + } + } +} + +impl Tx { + fn new() -> Self { + Self { + _usart: PhantomData, + _word: PhantomData, + } + } +} + +impl AsRef> for Serial { + #[inline(always)] + fn as_ref(&self) -> &Tx { + &self.tx + } +} + +impl AsRef> for Serial { + #[inline(always)] + fn as_ref(&self) -> &Rx { + &self.rx + } +} + +impl AsMut> for Serial { + #[inline(always)] + fn as_mut(&mut self) -> &mut Tx { + &mut self.tx + } +} + +impl AsMut> for Serial { + #[inline(always)] + fn as_mut(&mut self) -> &mut Rx { + &mut self.rx + } +} + +/// Serial receiver containing RX pin +pub struct URx { + inner: Rx, + pin: RX, +} + +/// Serial transmitter containing TX pin +pub struct UTx { + inner: Tx, + usart: USART, + pin: TX, +} + +impl URx { + fn new(pin: RX) -> Self { + Self { + inner: Rx::new(), + pin, + } + } + + pub fn erase(self) -> Rx { + Rx::new() + } + + pub fn join(self, tx: UTx) -> Serial + where + (TX, RX): Pins, + { + Serial { + usart: tx.usart, + pins: (tx.pin, self.pin), + tx: tx.inner, + rx: self.inner, + } + } +} + +impl UTx { + fn new(usart: USART, pin: TX) -> Self { + Self { + inner: Tx::new(), + usart, + pin, + } + } + + pub fn erase(self) -> Tx { + Tx::new() + } + + pub fn join(self, rx: URx) -> Serial + where + (TX, RX): Pins, + { + Serial { + usart: self.usart, + pins: (self.pin, rx.pin), + tx: self.inner, + rx: rx.inner, + } + } +} + +impl AsRef> for UTx { + #[inline(always)] + fn as_ref(&self) -> &Tx { + &self.inner + } +} + +impl Deref for UTx { + type Target = Tx; + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl AsRef> for URx { + #[inline(always)] + fn as_ref(&self) -> &Rx { + &self.inner + } +} + +impl Deref for URx { + type Target = Rx; + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl AsMut> for UTx { + #[inline(always)] + fn as_mut(&mut self) -> &mut Tx { + &mut self.inner + } +} + +impl DerefMut for UTx { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl AsMut> for URx { + #[inline(always)] + fn as_mut(&mut self) -> &mut Rx { + &mut self.inner + } +} + +impl DerefMut for URx { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +pub trait SerialExt: Sized + Instance { + fn serial( + self, + pins: (TX, RX), + config: impl Into, + clocks: &Clocks, + ) -> Result, config::InvalidConfig> + where + (TX, RX): Pins; + fn tx( + self, + tx_pin: TX, + config: impl Into, + clocks: &Clocks, + ) -> Result, config::InvalidConfig> + where + (TX, NoPin): Pins; + fn rx( + self, + rx_pin: RX, + config: impl Into, + clocks: &Clocks, + ) -> Result, config::InvalidConfig> + where + (NoPin, RX): Pins; +} + +impl SerialExt for USART { + fn serial( + self, + pins: (TX, RX), + config: impl Into, + clocks: &Clocks, + ) -> Result, config::InvalidConfig> + where + (TX, RX): Pins, + { + Serial::new(self, pins, config, clocks) + } + fn tx( + self, + tx_pin: TX, + config: impl Into, + clocks: &Clocks, + ) -> Result, config::InvalidConfig> + where + (TX, NoPin): Pins, + { + Serial::tx(self, tx_pin, config, clocks) + } + fn rx( + self, + rx_pin: RX, + config: impl Into, + clocks: &Clocks, + ) -> Result, config::InvalidConfig> + where + (NoPin, RX): Pins, + { + Serial::rx(self, rx_pin, config, clocks) + } +} + +impl Serial +where + (TX, RX): Pins, + USART: Instance, +{ + /* + StopBits::STOP0P5 and StopBits::STOP1P5 aren't supported when using UART + + STOP_A::STOP1 and STOP_A::STOP2 will be used, respectively + */ + pub fn new( + usart: USART, + mut pins: (TX, RX), + config: impl Into, + clocks: &Clocks, + ) -> Result { + use self::config::*; + + let config = config.into(); + unsafe { + // NOTE(unsafe) this reference will only be used for atomic writes with no side effects. + let rcc = &(*RCC::ptr()); + + // Enable clock. + USART::enable(rcc); + USART::reset(rcc); + } + + let pclk_freq = USART::clock(clocks).raw(); + let baud = config.baudrate.0; + + // The frequency to calculate USARTDIV is this: + // + // (Taken from STM32F411xC/E Reference Manual, + // Section 19.3.4, Equation 1) + // + // 16 bit oversample: OVER8 = 0 + // 8 bit oversample: OVER8 = 1 + // + // USARTDIV = (pclk) + // ------------------------ + // 8 x (2 - OVER8) x (baud) + // + // BUT, the USARTDIV has 4 "fractional" bits, which effectively + // means that we need to "correct" the equation as follows: + // + // USARTDIV = (pclk) * 16 + // ------------------------ + // 8 x (2 - OVER8) x (baud) + // + // When OVER8 is enabled, we can only use the lowest three + // fractional bits, so we'll need to shift those last four bits + // right one bit + + // Calculate correct baudrate divisor on the fly + let (over8, div) = if (pclk_freq / 16) >= baud { + // We have the ability to oversample to 16 bits, take + // advantage of it. + // + // We also add `baud / 2` to the `pclk_freq` to ensure + // rounding of values to the closest scale, rather than the + // floored behavior of normal integer division. + let div = (pclk_freq + (baud / 2)) / baud; + (false, div) + } else if (pclk_freq / 8) >= baud { + // We are close enough to pclk where we can only + // oversample 8. + // + // See note above regarding `baud` and rounding. + let div = ((pclk_freq * 2) + (baud / 2)) / baud; + + // Ensure the the fractional bits (only 3) are + // right-aligned. + let frac = div & 0xF; + let div = (div & !0xF) | (frac >> 1); + (true, div) + } else { + return Err(config::InvalidConfig); + }; + + unsafe { (*USART::ptr()).brr.write(|w| w.bits(div)) }; + + // Reset other registers to disable advanced USART features + unsafe { (*USART::ptr()).cr2.reset() }; + unsafe { (*USART::ptr()).cr3.reset() }; + + // Enable transmission and receiving + // and configure frame + unsafe { + (*USART::ptr()).cr1.write(|w| { + w.ue() + .set_bit() + .over8() + .bit(over8) + .te() + .set_bit() + .re() + .set_bit() + .m() + .bit(match config.wordlength { + WordLength::DataBits8 => false, + WordLength::DataBits9 => true, + }) + .pce() + .bit(!matches!(config.parity, Parity::ParityNone)) + .ps() + .bit(matches!(config.parity, Parity::ParityOdd)) + }) + }; + + match config.dma { + DmaConfig::Tx => unsafe { (*USART::ptr()).cr3.write(|w| w.dmat().enabled()) }, + DmaConfig::Rx => unsafe { (*USART::ptr()).cr3.write(|w| w.dmar().enabled()) }, + DmaConfig::TxRx => unsafe { + (*USART::ptr()) + .cr3 + .write(|w| w.dmar().enabled().dmat().enabled()) + }, + DmaConfig::None => {} + } + + pins.set_alt_mode(); + + Ok(Serial { + usart, + pins, + tx: Tx::new(), + rx: Rx::new(), + } + .config_stop(config)) + } + + pub fn release(mut self) -> (USART, (TX, RX)) { + self.pins.restore_mode(); + + (self.usart, (self.pins.0, self.pins.1)) + } +} + +impl Serial +where + (TX, NoPin): Pins, + USART: Instance, +{ + pub fn tx( + usart: USART, + tx_pin: TX, + config: impl Into, + clocks: &Clocks, + ) -> Result, config::InvalidConfig> { + Self::new(usart, (tx_pin, NoPin), config, clocks).map(|s| s.split().0) + } +} + +impl Serial +where + (NoPin, RX): Pins, + USART: Instance, +{ + pub fn rx( + usart: USART, + rx_pin: RX, + config: impl Into, + clocks: &Clocks, + ) -> Result, config::InvalidConfig> { + Self::new(usart, (NoPin, rx_pin), config, clocks).map(|s| s.split().1) + } +} + +impl Serial { + pub fn split(self) -> (Tx, Rx) { + (self.tx, self.rx) + } +} + +impl Serial { + pub fn split_nondestructive(self) -> (UTx, URx) { + (UTx::new(self.usart, self.pins.0), URx::new(self.pins.1)) + } +} + +impl Serial { + /// Converts this Serial into a version that can read and write `u16` values instead of `u8`s + /// + /// This can be used with a word length of 9 bits. + pub fn with_u16_data(self) -> Serial { + Serial { + usart: self.usart, + pins: self.pins, + tx: Tx::new(), + rx: Rx::new(), + } + } +} + +impl Serial { + /// Converts this Serial into a version that can read and write `u8` values instead of `u16`s + /// + /// This can be used with a word length of 8 bits. + pub fn with_u8_data(self) -> Serial { + Serial { + usart: self.usart, + pins: self.pins, + tx: Tx::new(), + rx: Rx::new(), + } + } +} + +unsafe impl PeriAddress for Rx { + #[inline(always)] + fn address(&self) -> u32 { + &(unsafe { &(*USART::ptr()) }.dr) as *const _ as u32 + } + + type MemSize = u8; +} + +unsafe impl PeriAddress for Tx { + #[inline(always)] + fn address(&self) -> u32 { + &(unsafe { &(*USART::ptr()) }.dr) as *const _ as u32 + } + + type MemSize = u8; +} + +impl Serial { + fn config_stop(self, config: config::Config) -> Self { + self.set_stopbits(config.stopbits); + self + } + + fn set_stopbits(&self, bits: config::StopBits) { + use crate::pac::uart4::cr2::STOP_A; + use config::StopBits; + + unsafe { &(*USART::ptr()) }.cr2.write(|w| { + w.stop().variant(match bits { + StopBits::STOP0P5 => STOP_A::Stop1, + StopBits::STOP1 => STOP_A::Stop1, + StopBits::STOP1P5 => STOP_A::Stop2, + StopBits::STOP2 => STOP_A::Stop2, + }) + }); + } +} + +use crate::pac::uart4 as uart_base; + +// Implemented by all USART instances +pub trait Instance: crate::Sealed + rcc::Enable + rcc::Reset + rcc::BusClock { + #[doc(hidden)] + fn ptr() -> *const uart_base::RegisterBlock; +} + +macro_rules! halUart { + ($USART:ty, $Serial:ident, $Tx:ident, $Rx:ident) => { + pub type $Serial = Serial<$USART, PINS, WORD>; + pub type $Tx = Tx<$USART, WORD>; + pub type $Rx = Rx<$USART, WORD>; + + impl Instance for $USART { + fn ptr() -> *const uart_base::RegisterBlock { + <$USART>::ptr() as *const _ + } + } + }; +} + +#[cfg(feature = "uart4")] +halUart! { pac::UART4, Serial4, Rx4, Tx4 } +#[cfg(feature = "uart5")] +halUart! { pac::UART5, Serial5, Rx5, Tx5 } +#[cfg(feature = "uart7")] +halUart! { pac::UART7, Serial7, Rx7, Tx7 } +#[cfg(feature = "uart8")] +halUart! { pac::UART8, Serial8, Rx8, Tx8 } +#[cfg(feature = "uart9")] +halUart! { pac::UART9, Serial9, Rx9, Tx9 } +#[cfg(feature = "uart10")] +halUart! { pac::UART10, Serial10, Rx10, Tx10 }