aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Trnka <[email protected]>2024-12-21 13:29:00 +0100
committerDaniel Trnka <[email protected]>2024-12-22 19:55:16 +0100
commita7983668da2947f7846f0abc4736708539aedc42 (patch)
tree073961668d62edf6006b6b253d9c88d38d5e4745
parent7b7ac1bd3ea2eb4897b8fcab706da968188bb700 (diff)
stm32/usart: half-duplex support for buffered usart
-rw-r--r--embassy-stm32/src/usart/buffered.rs100
1 files changed, 98 insertions, 2 deletions
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs
index 814be2858..7bbe01a00 100644
--- a/embassy-stm32/src/usart/buffered.rs
+++ b/embassy-stm32/src/usart/buffered.rs
@@ -12,8 +12,9 @@ use embassy_sync::waitqueue::AtomicWaker;
12#[cfg(not(any(usart_v1, usart_v2)))] 12#[cfg(not(any(usart_v1, usart_v2)))]
13use super::DePin; 13use super::DePin;
14use super::{ 14use super::{
15 clear_interrupt_flags, configure, rdr, reconfigure, send_break, set_baudrate, sr, tdr, Config, ConfigError, CtsPin, 15 clear_interrupt_flags, configure, half_duplex_set_rx_tx_before_write, rdr, reconfigure, send_break, set_baudrate,
16 Error, Info, Instance, Regs, RtsPin, RxPin, TxPin, 16 sr, tdr, Config, ConfigError, CtsPin, Duplex, Error, HalfDuplexConfig, HalfDuplexReadback, Info, Instance, Regs,
17 RtsPin, RxPin, TxPin,
17}; 18};
18use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; 19use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
19use crate::interrupt::{self, InterruptExt}; 20use crate::interrupt::{self, InterruptExt};
@@ -108,6 +109,8 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) {
108 }); 109 });
109 } 110 }
110 111
112 half_duplex_set_rx_tx_before_write(&r, state.half_duplex_readback.load(Ordering::Relaxed));
113
111 tdr(r).write_volatile(buf[0].into()); 114 tdr(r).write_volatile(buf[0].into());
112 tx_reader.pop_done(1); 115 tx_reader.pop_done(1);
113 } else { 116 } else {
@@ -126,6 +129,7 @@ pub(super) struct State {
126 tx_buf: RingBuffer, 129 tx_buf: RingBuffer,
127 tx_done: AtomicBool, 130 tx_done: AtomicBool,
128 tx_rx_refcount: AtomicU8, 131 tx_rx_refcount: AtomicU8,
132 half_duplex_readback: AtomicBool,
129} 133}
130 134
131impl State { 135impl State {
@@ -137,6 +141,7 @@ impl State {
137 tx_waker: AtomicWaker::new(), 141 tx_waker: AtomicWaker::new(),
138 tx_done: AtomicBool::new(true), 142 tx_done: AtomicBool::new(true),
139 tx_rx_refcount: AtomicU8::new(0), 143 tx_rx_refcount: AtomicU8::new(0),
144 half_duplex_readback: AtomicBool::new(false),
140 } 145 }
141 } 146 }
142} 147}
@@ -321,6 +326,84 @@ impl<'d> BufferedUart<'d> {
321 ) 326 )
322 } 327 }
323 328
329 /// Create a single-wire half-duplex Uart transceiver on a single Tx pin.
330 ///
331 /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin
332 /// (when it is available for your chip). There is no functional difference between these methods, as both
333 /// allow bidirectional communication.
334 ///
335 /// The TX pin is always released when no data is transmitted. Thus, it acts as a standard
336 /// I/O in idle or in reception. It means that the I/O must be configured so that TX is
337 /// configured as alternate function open-drain with an external pull-up
338 /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict
339 /// on the line must be managed by software (for instance by using a centralized arbiter).
340 #[doc(alias("HDSEL"))]
341 pub fn new_half_duplex<T: Instance>(
342 peri: impl Peripheral<P = T> + 'd,
343 tx: impl Peripheral<P = impl TxPin<T>> + 'd,
344 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
345 tx_buffer: &'d mut [u8],
346 rx_buffer: &'d mut [u8],
347 mut config: Config,
348 readback: HalfDuplexReadback,
349 half_duplex: HalfDuplexConfig,
350 ) -> Result<Self, ConfigError> {
351 #[cfg(not(any(usart_v1, usart_v2)))]
352 {
353 config.swap_rx_tx = false;
354 }
355 config.duplex = Duplex::Half(readback);
356
357 Self::new_inner(
358 peri,
359 None,
360 new_pin!(tx, half_duplex.af_type()),
361 None,
362 None,
363 None,
364 tx_buffer,
365 rx_buffer,
366 config,
367 )
368 }
369
370 /// Create a single-wire half-duplex Uart transceiver on a single Rx pin.
371 ///
372 /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin.
373 /// There is no functional difference between these methods, as both allow bidirectional communication.
374 ///
375 /// The pin is always released when no data is transmitted. Thus, it acts as a standard
376 /// I/O in idle or in reception.
377 /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict
378 /// on the line must be managed by software (for instance by using a centralized arbiter).
379 #[cfg(not(any(usart_v1, usart_v2)))]
380 #[doc(alias("HDSEL"))]
381 pub fn new_half_duplex_on_rx<T: Instance>(
382 peri: impl Peripheral<P = T> + 'd,
383 rx: impl Peripheral<P = impl RxPin<T>> + 'd,
384 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
385 tx_buffer: &'d mut [u8],
386 rx_buffer: &'d mut [u8],
387 mut config: Config,
388 readback: HalfDuplexReadback,
389 half_duplex: HalfDuplexConfig,
390 ) -> Result<Self, ConfigError> {
391 config.swap_rx_tx = true;
392 config.duplex = Duplex::Half(readback);
393
394 Self::new_inner(
395 peri,
396 new_pin!(rx, half_duplex.af_type()),
397 None,
398 None,
399 None,
400 None,
401 tx_buffer,
402 rx_buffer,
403 config,
404 )
405 }
406
324 fn new_inner<T: Instance>( 407 fn new_inner<T: Instance>(
325 _peri: impl Peripheral<P = T> + 'd, 408 _peri: impl Peripheral<P = T> + 'd,
326 rx: Option<PeripheralRef<'d, AnyPin>>, 409 rx: Option<PeripheralRef<'d, AnyPin>>,
@@ -336,6 +419,11 @@ impl<'d> BufferedUart<'d> {
336 let state = T::buffered_state(); 419 let state = T::buffered_state();
337 let kernel_clock = T::frequency(); 420 let kernel_clock = T::frequency();
338 421
422 state.half_duplex_readback.store(
423 config.duplex == Duplex::Half(HalfDuplexReadback::Readback),
424 Ordering::Relaxed,
425 );
426
339 let mut this = Self { 427 let mut this = Self {
340 rx: BufferedUartRx { 428 rx: BufferedUartRx {
341 info, 429 info,
@@ -381,12 +469,20 @@ impl<'d> BufferedUart<'d> {
381 w.set_ctse(self.tx.cts.is_some()); 469 w.set_ctse(self.tx.cts.is_some());
382 #[cfg(not(any(usart_v1, usart_v2)))] 470 #[cfg(not(any(usart_v1, usart_v2)))]
383 w.set_dem(self.tx.de.is_some()); 471 w.set_dem(self.tx.de.is_some());
472 w.set_hdsel(config.duplex.is_half());
384 }); 473 });
385 configure(info, self.rx.kernel_clock, &config, true, true)?; 474 configure(info, self.rx.kernel_clock, &config, true, true)?;
386 475
387 info.regs.cr1().modify(|w| { 476 info.regs.cr1().modify(|w| {
388 w.set_rxneie(true); 477 w.set_rxneie(true);
389 w.set_idleie(true); 478 w.set_idleie(true);
479
480 if config.duplex.is_half() {
481 // The te and re bits will be set by write, read and flush methods.
482 // Receiver should be enabled by default for Half-Duplex.
483 w.set_te(false);
484 w.set_re(true);
485 }
390 }); 486 });
391 487
392 info.interrupt.unpend(); 488 info.interrupt.unpend();