From 1c285b15e615656b2809a3b5592f7af271b43fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Sat, 8 Jan 2022 14:40:41 +0100 Subject: [PATCH 1/2] Support pin type erasure in Serial There are many possible pin combinations for a Serial peripheral. Many support at least one alternative pin group to be used, and in addition to that, each pin can also be configured differently. For instance, instead of the common Output with an Input, one could also use Output and Input, e.g. when interfacing with a 1-Wire bus. This pin information is already lost when splitting the Serial object into Rx/Tx. In order to allow a reunification into a Serial object which represents ownership of both Tx and Rx, we need a Serial variant which has that pin information erased. --- src/serial.rs | 112 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 104 insertions(+), 8 deletions(-) diff --git a/src/serial.rs b/src/serial.rs index 881bd595..1d39d285 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -185,8 +185,12 @@ use crate::pac::usart1 as uart_base; /// Serial abstraction pub struct Serial { - usart: USART, pins: PINS, + inner: ErasedSerial, +} + +pub struct ErasedSerial { + usart: USART, tx: Tx, rx: Rx, } @@ -224,7 +228,7 @@ impl Tx { } } -impl Serial +impl ErasedSerial where USART: Instance, { @@ -348,15 +352,81 @@ where } } + /// Separates the serial struct into separate channel objects for sending (Tx) and + /// receiving (Rx) + pub fn split(self) -> (Tx, Rx) { + (self.tx, self.rx) + } +} + +impl Serial +where + USART: Instance, +{ /// Returns ownership of the borrowed register handles pub fn release(self) -> (USART, PINS) { - (self.usart, self.pins) + (self.inner.usart, self.pins) + } + + /// Erase the pins used for the Serial from the type + pub fn erase(self) -> ErasedSerial { + self.inner } /// Separates the serial struct into separate channel objects for sending (Tx) and /// receiving (Rx) + #[inline(always)] pub fn split(self) -> (Tx, Rx) { - (self.tx, self.rx) + self.inner.split() + } + + /// Reconfigure the USART instance. + /// + /// If a transmission is currently in progress, this returns + /// [`nb::Error::WouldBlock`]. + #[inline(always)] + pub fn reconfigure(&mut self, config: impl Into, clocks: Clocks) -> nb::Result<(), ()> { + self.inner.reconfigure(config, clocks) + } + + /// Starts listening to the USART by enabling the _Received data + /// ready to be read (RXNE)_ interrupt and _Transmit data + /// register empty (TXE)_ interrupt + #[inline(always)] + pub fn listen(&mut self, event: Event) { + self.inner.listen(event) + } + + /// Stops listening to the USART by disabling the _Received data + /// ready to be read (RXNE)_ interrupt and _Transmit data + /// register empty (TXE)_ interrupt + #[inline(always)] + pub fn unlisten(&mut self, event: Event) { + self.inner.unlisten(event) + } + + /// Returns true if the line idle status is set + #[inline(always)] + pub fn is_idle(&self) -> bool { + self.inner.is_idle() + } + + /// Returns true if the tx register is empty (and can accept data) + #[inline(always)] + pub fn is_tx_empty(&self) -> bool { + self.inner.is_tx_empty() + } + + /// Returns true if the rx register is not empty (and can be read) + #[inline(always)] + pub fn is_rx_not_empty(&self) -> bool { + self.inner.is_rx_not_empty() + } + + /// Clear idle line interrupt flag + #[inline(always)] + pub fn clear_idle_interrupt(&self) { + self.inner.clear_idle_interrupt() } } @@ -406,12 +476,12 @@ macro_rules! hal { PINS: Pins<$USARTX>, { #[allow(unused_unsafe)] - Serial { usart, pins, tx: Tx::new(), rx: Rx::new() }.init(config.into(), clocks, || { + Serial { pins, inner: ErasedSerial{ usart, tx: Tx::new(), rx: Rx::new() }.init(config.into(), clocks, || { mapr.modify_mapr(|_, w| unsafe { #[allow(clippy::redundant_closure_call)] w.$usartX_remap().$bit(($closure)(PINS::REMAP)) }) - }) + }) } } } @@ -557,7 +627,7 @@ where } } -impl crate::hal::serial::Read for Serial +impl crate::hal::serial::Read for ErasedSerial where USART: Instance, { @@ -568,7 +638,7 @@ where } } -impl crate::hal::serial::Write for Serial +impl crate::hal::serial::Write for ErasedSerial where USART: Instance, { @@ -583,6 +653,32 @@ where } } +impl crate::hal::serial::Read for Serial +where + USART: Instance, +{ + type Error = Error; + + fn read(&mut self) -> nb::Result { + self.inner.rx.read() + } +} + +impl crate::hal::serial::Write for Serial +where + USART: Instance, +{ + type Error = Infallible; + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.inner.tx.flush() + } + + fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { + self.inner.tx.write(byte) + } +} + impl core::fmt::Write for Tx where Tx: embedded_hal::serial::Write, From 8721f6966fb977f3be7861df085e0e9f11fa5128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Sat, 8 Jan 2022 14:44:44 +0100 Subject: [PATCH 2/2] Support reunite in serial::{Rx,Tx} This becomes relevant as more functions like reconfigure are added to the Serial structs. We use the Tx to stash away the usart instance used by the (Erased-)Serial struct, but this choice is arbitrary and irrelevant because it is zero-sized anyway. Fixes #386. --- examples/serial.rs | 8 +++++++ src/serial.rs | 58 ++++++++++++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/examples/serial.rs b/examples/serial.rs index c1b2ef49..95cc78b7 100644 --- a/examples/serial.rs +++ b/examples/serial.rs @@ -88,5 +88,13 @@ fn main() -> ! { assert_eq!(received, sent); asm::bkpt(); + // And later reunite it again + let mut serial = tx.reunite(rx); + let sent = b'Z'; + block!(serial.write(sent)).ok(); + let received = block!(serial.read()).unwrap(); + assert_eq!(received, sent); + asm::bkpt(); + loop {} } diff --git a/src/serial.rs b/src/serial.rs index 1d39d285..b3b11f11 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -190,7 +190,6 @@ pub struct Serial { } pub struct ErasedSerial { - usart: USART, tx: Tx, rx: Rx, } @@ -209,7 +208,7 @@ pub struct Rx { /// Serial transmitter pub struct Tx { - _usart: PhantomData, + usart: USART, } impl Rx { @@ -218,13 +217,21 @@ impl Rx { _usart: PhantomData, } } + + /// Reunite the two halves of a split serial + pub fn reunite(self, tx: Tx) -> ErasedSerial { + ErasedSerial { rx: self, tx } + } } impl Tx { - fn new() -> Self { - Self { - _usart: PhantomData, - } + fn new(usart: USART) -> Self { + Self { usart } + } + + /// Reunite the two halves of a split serial + pub fn reunite(self, rx: Rx) -> ErasedSerial { + ErasedSerial { tx: self, rx } } } @@ -244,7 +251,8 @@ where // UE: enable USART // RE: enable receiver // TE: enable transceiver - self.usart + self.tx + .usart .cr1 .modify(|_r, w| w.ue().set_bit().re().set_bit().te().set_bit()); @@ -255,7 +263,7 @@ where // Configure baud rate let brr = USART::get_frequency(&clocks).0 / config.baudrate.0; assert!(brr >= 16, "impossible baud rate"); - self.usart.brr.write(|w| unsafe { w.bits(brr) }); + self.tx.usart.brr.write(|w| unsafe { w.bits(brr) }); // Configure parity and word length // Unlike most uart devices, the "word length" of this usart device refers to @@ -267,7 +275,7 @@ where Parity::ParityEven => (true, true, false), Parity::ParityOdd => (true, true, true), }; - self.usart.cr1.modify(|_r, w| { + self.tx.usart.cr1.modify(|_r, w| { w.m() .bit(word_length) .ps() @@ -283,7 +291,7 @@ where StopBits::STOP2 => 0b10, StopBits::STOP1P5 => 0b11, }; - self.usart.cr2.modify(|_r, w| w.stop().bits(stop_bits)); + self.tx.usart.cr2.modify(|_r, w| w.stop().bits(stop_bits)); } /// Reconfigure the USART instance. @@ -295,7 +303,7 @@ where config: impl Into, clocks: Clocks, ) -> nb::Result<(), Infallible> { - let sr = self.usart.sr.read(); + let sr = self.tx.usart.sr.read(); // if we're currently busy transmitting, we have to wait until that is // over -- regarding reception, we assume that the caller -- with // exclusive access to the Serial instance due to &mut self -- knows @@ -312,9 +320,9 @@ where /// register empty (TXE)_ interrupt pub fn listen(&mut self, event: Event) { match event { - Event::Rxne => self.usart.cr1.modify(|_, w| w.rxneie().set_bit()), - Event::Txe => self.usart.cr1.modify(|_, w| w.txeie().set_bit()), - Event::Idle => self.usart.cr1.modify(|_, w| w.idleie().set_bit()), + Event::Rxne => self.tx.usart.cr1.modify(|_, w| w.rxneie().set_bit()), + Event::Txe => self.tx.usart.cr1.modify(|_, w| w.txeie().set_bit()), + Event::Idle => self.tx.usart.cr1.modify(|_, w| w.idleie().set_bit()), } } @@ -323,25 +331,25 @@ where /// register empty (TXE)_ interrupt pub fn unlisten(&mut self, event: Event) { match event { - Event::Rxne => self.usart.cr1.modify(|_, w| w.rxneie().clear_bit()), - Event::Txe => self.usart.cr1.modify(|_, w| w.txeie().clear_bit()), - Event::Idle => self.usart.cr1.modify(|_, w| w.idleie().clear_bit()), + Event::Rxne => self.tx.usart.cr1.modify(|_, w| w.rxneie().clear_bit()), + Event::Txe => self.tx.usart.cr1.modify(|_, w| w.txeie().clear_bit()), + Event::Idle => self.tx.usart.cr1.modify(|_, w| w.idleie().clear_bit()), } } /// Returns true if the line idle status is set pub fn is_idle(&self) -> bool { - self.usart.sr.read().idle().bit_is_set() + self.tx.usart.sr.read().idle().bit_is_set() } /// Returns true if the tx register is empty (and can accept data) pub fn is_tx_empty(&self) -> bool { - self.usart.sr.read().txe().bit_is_set() + self.tx.usart.sr.read().txe().bit_is_set() } /// Returns true if the rx register is not empty (and can be read) pub fn is_rx_not_empty(&self) -> bool { - self.usart.sr.read().rxne().bit_is_set() + self.tx.usart.sr.read().rxne().bit_is_set() } /// Clear idle line interrupt flag @@ -365,7 +373,7 @@ where { /// Returns ownership of the borrowed register handles pub fn release(self) -> (USART, PINS) { - (self.inner.usart, self.pins) + (self.inner.tx.usart, self.pins) } /// Erase the pins used for the Serial from the type @@ -385,7 +393,11 @@ where /// If a transmission is currently in progress, this returns /// [`nb::Error::WouldBlock`]. #[inline(always)] - pub fn reconfigure(&mut self, config: impl Into, clocks: Clocks) -> nb::Result<(), ()> { + pub fn reconfigure( + &mut self, + config: impl Into, + clocks: Clocks, + ) -> nb::Result<(), Infallible> { self.inner.reconfigure(config, clocks) } @@ -476,7 +488,7 @@ macro_rules! hal { PINS: Pins<$USARTX>, { #[allow(unused_unsafe)] - Serial { pins, inner: ErasedSerial{ usart, tx: Tx::new(), rx: Rx::new() }.init(config.into(), clocks, || { + Serial { pins, inner: ErasedSerial{ tx: Tx::new(usart), rx: Rx::new() }.init(config.into(), clocks, || { mapr.modify_mapr(|_, w| unsafe { #[allow(clippy::redundant_closure_call)] w.$usartX_remap().$bit(($closure)(PINS::REMAP))