Skip to content

Commit

Permalink
Merge #514
Browse files Browse the repository at this point in the history
514: Serial Tx, Rx containing pins r=therealprof a=burrbull

Related to #505 

Instead of changing API I added alternative.

So now to can choose between `Tx`, `Rx` with erased information about pins and `UTx`, `URx` which can join to restore `Serial`

Co-authored-by: Andrey Zgarbul <zgarbul.andrey@gmail.com>
  • Loading branch information
bors[bot] and burrbull authored Aug 15, 2022
2 parents 0db12eb + 6f4b8b4 commit 1e1c59c
Show file tree
Hide file tree
Showing 4 changed files with 338 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Added

- Serial Tx, Rx containing pins [#514]
- Implementation of From trait for Pin-to-PartiallyErasedPin [#507]
- Implementation of From trait for Pin-to-ErasedPin [#507]
- Implementation of From trait for PartiallyErasedPin-to-ErasedPin [#507]
Expand All @@ -38,6 +39,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
[#507]: https://github.com/stm32-rs/stm32f4xx-hal/pull/507
[#508]: https://github.com/stm32-rs/stm32f4xx-hal/pull/508
[#510]: https://github.com/stm32-rs/stm32f4xx-hal/pull/510
[#510]: https://github.com/stm32-rs/stm32f4xx-hal/pull/514

## [v0.13.2] - 2022-05-16

Expand Down
229 changes: 229 additions & 0 deletions src/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,123 @@ impl<USART, PINS, WORD> AsMut<Rx<USART, WORD>> for Serial<USART, PINS, WORD> {
}
}

/// Serial receiver containing RX pin
pub struct URx<USART, RX, WORD = u8> {
_usart: PhantomData<USART>,
pin: RX,
_word: PhantomData<WORD>,
}

/// Serial transmitter containing TX pin
pub struct UTx<USART, TX, WORD = u8> {
usart: USART,
pin: TX,
_word: PhantomData<WORD>,
}

impl<USART: Instance, RX, WORD> URx<USART, RX, WORD> {
fn new(pin: RX) -> Self {
Self {
_usart: PhantomData,
pin,
_word: PhantomData,
}
}

pub fn erase(self) -> Rx<USART, WORD> {
Rx::new()
}

/// 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<USART: Instance, TX, WORD> UTx<USART, TX, WORD> {
fn new(usart: USART, pin: TX) -> Self {
Self {
usart,
pin,
_word: PhantomData,
}
}

pub fn erase(self) -> Tx<USART, WORD> {
Tx::new()
}

pub fn join<RX>(self, rx: URx<USART, RX, WORD>) -> Serial<USART, (TX, RX), WORD>
where
(TX, RX): Pins<USART>,
{
Serial {
usart: self.usart,
pins: (self.pin, rx.pin),
tx: Tx::new(),
rx: Rx::new(),
}
}

/// 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.
// TODO: replace with "self.usart"
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() }
}
}

pub trait SerialExt: Sized + Instance {
fn serial<TX, RX, WORD>(
self,
Expand Down Expand Up @@ -614,6 +731,12 @@ impl<USART: Instance, PINS, WORD> Serial<USART, PINS, WORD> {
}
}

impl<USART: Instance, TX, RX, WORD> Serial<USART, (TX, RX), WORD> {
pub fn split_nondestructive(self) -> (UTx<USART, TX, WORD>, URx<USART, RX, WORD>) {
(UTx::new(self.usart, self.pins.0), URx::new(self.pins.1))
}
}

impl<USART: Instance, PINS> Serial<USART, PINS, u8> {
/// Converts this Serial into a version that can read and write `u16` values instead of `u8`s
///
Expand Down Expand Up @@ -912,3 +1035,109 @@ impl<USART: Instance> Tx<USART, u16> {
nb::block!(self.flush())
}
}

impl<USART: Instance, TX> fmt::Write for UTx<USART, 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<USART: Instance, RX> URx<USART, RX, u8> {
fn read(&mut self) -> nb::Result<u8, Error> {
// Delegate to the Read<u16> implementation, then truncate to 8 bits
Rx::<USART, u16>::new().read().map(|word16| word16 as u8)
}
}

impl<USART: Instance, RX> URx<USART, RX, u16> {
fn read(&mut self) -> nb::Result<u16, Error> {
// 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<USART: Instance, TX> UTx<USART, TX, u8> {
fn write(&mut self, word: u8) -> nb::Result<(), Error> {
// Delegate to u16 version
Tx::<USART, u16>::new().write(u16::from(word))
}

fn flush(&mut self) -> nb::Result<(), Error> {
// Delegate to u16 version
Tx::<USART, u16>::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<USART: Instance, TX> UTx<USART, TX, u16> {
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())
}
}
58 changes: 55 additions & 3 deletions src/serial/hal_02.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mod nb {
use super::super::{Error, Instance, Rx, Serial, Tx};
use super::super::{Error, Instance, Rx, Serial, Tx, URx, UTx};
use embedded_hal::serial::{Read, Write};

impl<USART, PINS, WORD> Read<WORD> for Serial<USART, PINS, WORD>
Expand All @@ -22,6 +22,14 @@ mod nb {
}
}

impl<USART: Instance, RX> Read<u8> for URx<USART, RX, u8> {
type Error = Error;

fn read(&mut self) -> nb::Result<u8, Self::Error> {
self.read()
}
}

/// Reads 9-bit words from the UART/USART
///
/// If the UART/USART was configured with `WordLength::DataBits9`, the returned value will contain
Expand All @@ -35,6 +43,14 @@ mod nb {
}
}

impl<USART: Instance, RX> Read<u16> for URx<USART, RX, u16> {
type Error = Error;

fn read(&mut self) -> nb::Result<u16, Self::Error> {
self.read()
}
}

impl<USART, PINS, WORD> Write<WORD> for Serial<USART, PINS, WORD>
where
USART: Instance,
Expand All @@ -51,7 +67,7 @@ mod nb {
}
}

impl<USART: Instance> Write<u8> for Tx<USART, u8> {
impl<USART: Instance, TX> Write<u8> for UTx<USART, TX, u8> {
type Error = Error;

fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
Expand Down Expand Up @@ -79,10 +95,22 @@ mod nb {
self.flush()
}
}

impl<USART: Instance, TX> Write<u16> for UTx<USART, TX, u16> {
type Error = Error;

fn write(&mut self, word: u16) -> nb::Result<(), Self::Error> {
self.write(word)
}

fn flush(&mut self) -> nb::Result<(), Self::Error> {
self.flush()
}
}
}

mod blocking {
use super::super::{Error, Instance, Serial, Tx};
use super::super::{Error, Instance, Serial, Tx, UTx};
use embedded_hal::blocking::serial::Write;

impl<USART: Instance> Write<u8> for Tx<USART, u8> {
Expand All @@ -97,6 +125,18 @@ mod blocking {
}
}

impl<USART: Instance, TX> Write<u8> for UTx<USART, TX, u8> {
type Error = Error;

fn bwrite_all(&mut self, bytes: &[u8]) -> Result<(), Self::Error> {
self.bwrite_all(bytes)
}

fn bflush(&mut self) -> Result<(), Self::Error> {
self.bflush()
}
}

impl<USART: Instance, PINS> Write<u8> for Serial<USART, PINS, u8> {
type Error = Error;

Expand All @@ -121,6 +161,18 @@ mod blocking {
}
}

impl<USART: Instance, TX> Write<u16> for UTx<USART, TX, u16> {
type Error = Error;

fn bwrite_all(&mut self, slice: &[u16]) -> Result<(), Self::Error> {
self.bwrite_all(slice)
}

fn bflush(&mut self) -> Result<(), Self::Error> {
self.bflush()
}
}

impl<USART: Instance, PINS> Write<u16> for Serial<USART, PINS, u16> {
type Error = Error;

Expand Down
Loading

0 comments on commit 1e1c59c

Please sign in to comment.