From dc295fa1db1245ca5ea5241104140ae1e62ea5ac Mon Sep 17 00:00:00 2001 From: lights0123 Date: Tue, 26 Dec 2023 16:34:04 -0500 Subject: [PATCH] stm32: add half duplex USART driver --- embassy-stm32/src/usart/mod.rs | 87 +++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 8a0c85d2c..ea727b010 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -743,7 +743,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { T::enable_and_reset(); T::enable_and_reset(); - Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config) + Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config) } /// Create a new bidirectional UART with request-to-send and clear-to-send pins @@ -770,7 +770,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { w.set_rtse(true); w.set_ctse(true); }); - Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config) + Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config) } #[cfg(not(any(usart_v1, usart_v2)))] @@ -795,10 +795,76 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { T::regs().cr3().write(|w| { w.set_dem(true); }); - Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config) + Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config) } - fn new_inner( + /// Create a single-wire half-duplex Uart transceiver on a single Tx pin. + /// + /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin. + /// There is no functional difference between these methods, as both allow bidirectional communication. + /// + /// The pin is always released when no data is transmitted. Thus, it acts as a standard + /// I/O in idle or in reception. + /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict + /// on the line must be managed by software (for instance by using a centralized arbiter). + #[cfg(not(any(usart_v1, usart_v2)))] + #[doc(alias("HDSEL"))] + pub fn new_half_duplex( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + mut config: Config, + ) -> Result { + // UartRx and UartTx have one refcount ea. + T::enable_and_reset(); + T::enable_and_reset(); + + config.swap_rx_tx = false; + + into_ref!(peri, tx, tx_dma, rx_dma); + + T::regs().cr3().write(|w| w.set_hdsel(true)); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + + Self::new_inner(peri, tx_dma, rx_dma, config) + } + + /// Create a single-wire half-duplex Uart transceiver on a single Rx pin. + /// + /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin. + /// There is no functional difference between these methods, as both allow bidirectional communication. + /// + /// The pin is always released when no data is transmitted. Thus, it acts as a standard + /// I/O in idle or in reception. + /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict + /// on the line must be managed by software (for instance by using a centralized arbiter). + #[cfg(not(any(usart_v1, usart_v2)))] + #[doc(alias("HDSEL"))] + pub fn new_half_duplex_on_rx( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + mut config: Config, + ) -> Result { + // UartRx and UartTx have one refcount ea. + T::enable_and_reset(); + T::enable_and_reset(); + + config.swap_rx_tx = true; + + into_ref!(peri, rx, tx_dma, rx_dma); + + T::regs().cr3().write(|w| w.set_hdsel(true)); + rx.set_as_af(rx.af_num(), AFType::OutputPushPull); + + Self::new_inner(peri, tx_dma, rx_dma, config) + } + + fn new_inner_configure( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, @@ -808,8 +874,6 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { ) -> Result { into_ref!(peri, rx, tx, tx_dma, rx_dma); - let r = T::regs(); - // Some chips do not have swap_rx_tx bit cfg_if::cfg_if! { if #[cfg(any(usart_v3, usart_v4))] { @@ -827,6 +891,17 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } + Self::new_inner(peri, tx_dma, rx_dma, config) + } + + fn new_inner( + peri: PeripheralRef<'d, T>, + tx_dma: PeripheralRef<'d, TxDma>, + rx_dma: PeripheralRef<'d, RxDma>, + config: Config, + ) -> Result { + let r = T::regs(); + configure(r, &config, T::frequency(), T::KIND, true, true)?; T::Interrupt::unpend();