From 09a1b94a0b9f95f0087ad6aed7d99062bd9132f8 Mon Sep 17 00:00:00 2001 From: Danilo Bargen Date: Fri, 17 Jan 2020 15:03:41 +0100 Subject: [PATCH 1/4] Implement Read / Write on Serial type --- CHANGELOG.md | 1 + examples/serial.rs | 21 ++++++++---- src/serial.rs | 81 +++++++++++++++++++++++++++++++++++----------- 3 files changed, 78 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb1f234d..b2cb8bc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - Add `InputPin` impl for generic open drain outputs +- Implement `Read` / `Write` for `Serial` (#171) ## [v0.5.2] - 2019-12-15 diff --git a/examples/serial.rs b/examples/serial.rs index db29af16..e64fa0fc 100644 --- a/examples/serial.rs +++ b/examples/serial.rs @@ -59,7 +59,7 @@ fn main() -> ! { // Set up the usart device. Taks ownership over the USART register and tx/rx pins. The rest of // the registers are used to enable and configure the device. - let serial = Serial::usart3( + let mut serial = Serial::usart3( p.USART3, (tx, rx), &mut afio.mapr, @@ -68,16 +68,13 @@ fn main() -> ! { &mut rcc.apb1, ); - // Split the serial struct into a receiving and a transmitting part - let (mut tx, mut rx) = serial.split(); + // Loopback test. Write `X` and wait until the write is successful. let sent = b'X'; - - // Write `X` and wait until the write is successful - block!(tx.write(sent)).ok(); + block!(serial.write(sent)).ok(); // Read the byte that was just sent. Blocks until the read is complete - let received = block!(rx.read()).unwrap(); + let received = block!(serial.read()).unwrap(); // Since we have connected tx and rx, the byte we sent should be the one we received assert_eq!(received, sent); @@ -85,5 +82,15 @@ fn main() -> ! { // Trigger a breakpoint to allow us to inspect the values asm::bkpt(); + + // You can also split the serial struct into a receiving and a transmitting part + let (mut tx, mut rx) = serial.split(); + let sent = b'Y'; + block!(tx.write(sent)).ok(); + let received = block!(rx.read()).unwrap(); + assert_eq!(received, sent); + asm::bkpt(); + + loop {} } diff --git a/src/serial.rs b/src/serial.rs index 1a2d1712..941a4a8b 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -1,9 +1,10 @@ //! # Serial Communication (USART) +//! //! This module contains the functions to utilize the USART (Universal //! synchronous asynchronous receiver transmitter) //! -//! //! ## Example usage: +//! //! ```rust //! // prelude: create handles to the peripherals and registers //! let p = crate::pac::Peripherals::take().unwrap(); @@ -113,6 +114,16 @@ impl Pins for (PD8>, PD9>) { const REMAP: u8 = 0b11; } +/// Internal trait for the serial read / write logic. +/// +/// Note that reading / writing is done on the USART peripheral, not on the +/// rx / tx pins! +trait UsartReadWrite { + fn read() -> nb::Result; + fn write(byte: Word) -> nb::Result<(), Infallible>; + fn flush() -> nb::Result<(), Infallible>; +} + pub enum Parity { ParityNone, ParityEven, @@ -346,10 +357,8 @@ macro_rules! hal { } } - impl crate::hal::serial::Read for Rx<$USARTX> { - type Error = Error; - - fn read(&mut self) -> nb::Result { + impl UsartReadWrite for $USARTX { + fn read() -> nb::Result { // NOTE(unsafe) atomic read with no side effects let sr = unsafe { (*$USARTX::ptr()).sr.read() }; @@ -389,38 +398,74 @@ macro_rules! hal { } } } - } - - impl crate::hal::serial::Write for Tx<$USARTX> { - type Error = Infallible; - fn flush(&mut self) -> nb::Result<(), Self::Error> { + fn write(byte: u8) -> nb::Result<(), Infallible> { // NOTE(unsafe) atomic read with no side effects let sr = unsafe { (*$USARTX::ptr()).sr.read() }; - if sr.tc().bit_is_set() { + if sr.txe().bit_is_set() { + // NOTE(unsafe) atomic write to stateless register + // NOTE(write_volatile) 8-bit write that's not possible through the svd2rust API + unsafe { + ptr::write_volatile(&(*$USARTX::ptr()).dr as *const _ as *mut _, byte) + } Ok(()) } else { Err(nb::Error::WouldBlock) } } - fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { + fn flush() -> nb::Result<(), Infallible> { // NOTE(unsafe) atomic read with no side effects let sr = unsafe { (*$USARTX::ptr()).sr.read() }; - if sr.txe().bit_is_set() { - // NOTE(unsafe) atomic write to stateless register - // NOTE(write_volatile) 8-bit write that's not possible through the svd2rust API - unsafe { - ptr::write_volatile(&(*$USARTX::ptr()).dr as *const _ as *mut _, byte) - } + if sr.tc().bit_is_set() { Ok(()) } else { Err(nb::Error::WouldBlock) } } } + + impl crate::hal::serial::Read for Rx<$USARTX> { + type Error = Error; + + fn read(&mut self) -> nb::Result { + $USARTX::read() + } + } + + impl crate::hal::serial::Write for Tx<$USARTX> { + type Error = Infallible; + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + $USARTX::flush() + } + fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { + $USARTX::write(byte) + } + } + + impl crate::hal::serial::Read for Serial<$USARTX, PINS> { + type Error = Error; + + fn read(&mut self) -> nb::Result { + $USARTX::read() + } + } + + impl crate::hal::serial::Write for Serial<$USARTX, PINS> { + type Error = Infallible; + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + $USARTX::flush() + } + + fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { + $USARTX::write(byte) + } + } + )+ } } From ecbc653faa456ece66d4e9b0d08aa84acc40b6cc Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Fri, 17 Jan 2020 20:02:23 +0300 Subject: [PATCH 2/4] Remove Word trait parameter --- src/serial.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serial.rs b/src/serial.rs index 941a4a8b..57e03592 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -118,9 +118,9 @@ impl Pins for (PD8>, PD9>) { /// /// Note that reading / writing is done on the USART peripheral, not on the /// rx / tx pins! -trait UsartReadWrite { - fn read() -> nb::Result; - fn write(byte: Word) -> nb::Result<(), Infallible>; +trait UsartReadWrite { + fn read() -> nb::Result; + fn write(byte: u8) -> nb::Result<(), Infallible>; fn flush() -> nb::Result<(), Infallible>; } @@ -357,7 +357,7 @@ macro_rules! hal { } } - impl UsartReadWrite for $USARTX { + impl UsartReadWrite for $USARTX { fn read() -> nb::Result { // NOTE(unsafe) atomic read with no side effects let sr = unsafe { (*$USARTX::ptr()).sr.read() }; From 5b7dc0261117b1b83d8e9680cc2b5d8ef0fbf2e2 Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Fri, 17 Jan 2020 20:09:50 +0300 Subject: [PATCH 3/4] Deduplicate USART driver code --- src/serial.rs | 156 +++++++++++++++++++++++--------------------------- 1 file changed, 73 insertions(+), 83 deletions(-) diff --git a/src/serial.rs b/src/serial.rs index 57e03592..4b13b075 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -40,6 +40,7 @@ use core::marker::PhantomData; use core::ptr; use core::sync::atomic::{self, Ordering}; +use core::ops::Deref; use nb; use crate::pac::{USART1, USART2, USART3}; @@ -115,14 +116,73 @@ impl Pins for (PD8>, PD9>) { } /// Internal trait for the serial read / write logic. -/// -/// Note that reading / writing is done on the USART peripheral, not on the -/// rx / tx pins! -trait UsartReadWrite { - fn read() -> nb::Result; - fn write(byte: u8) -> nb::Result<(), Infallible>; - fn flush() -> nb::Result<(), Infallible>; +trait UsartReadWrite: Deref { + fn read(&self) -> nb::Result { + let sr = self.sr.read(); + + // Check for any errors + let err = if sr.pe().bit_is_set() { + Some(Error::Parity) + } else if sr.fe().bit_is_set() { + Some(Error::Framing) + } else if sr.ne().bit_is_set() { + Some(Error::Noise) + } else if sr.ore().bit_is_set() { + Some(Error::Overrun) + } else { + None + }; + + if let Some(err) = err { + // Some error occured. In order to clear that error flag, you have to + // do a read from the sr register followed by a read from the dr + // register + // NOTE(read_volatile) see `write_volatile` below + unsafe { + ptr::read_volatile(&self.sr as *const _ as *const _); + ptr::read_volatile(&self.dr as *const _ as *const _); + } + Err(nb::Error::Other(err)) + } else { + // Check if a byte is available + if sr.rxne().bit_is_set() { + // Read the received byte + // NOTE(read_volatile) see `write_volatile` below + Ok(unsafe { + ptr::read_volatile(&self.dr as *const _ as *const _) + }) + } else { + Err(nb::Error::WouldBlock) + } + } + } + + fn write(&self, byte: u8) -> nb::Result<(), Infallible> { + let sr = self.sr.read(); + + if sr.txe().bit_is_set() { + // NOTE(unsafe) atomic write to stateless register + // NOTE(write_volatile) 8-bit write that's not possible through the svd2rust API + unsafe { + ptr::write_volatile(&self.dr as *const _ as *mut _, byte) + } + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } + + fn flush(&self) -> nb::Result<(), Infallible> { + let sr = self.sr.read(); + + if sr.tc().bit_is_set() { + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } } +impl UsartReadWrite for &crate::stm32::usart1::RegisterBlock {} pub enum Parity { ParityNone, @@ -357,81 +417,11 @@ macro_rules! hal { } } - impl UsartReadWrite for $USARTX { - fn read() -> nb::Result { - // NOTE(unsafe) atomic read with no side effects - let sr = unsafe { (*$USARTX::ptr()).sr.read() }; - - // Check for any errors - let err = if sr.pe().bit_is_set() { - Some(Error::Parity) - } else if sr.fe().bit_is_set() { - Some(Error::Framing) - } else if sr.ne().bit_is_set() { - Some(Error::Noise) - } else if sr.ore().bit_is_set() { - Some(Error::Overrun) - } else { - None - }; - - if let Some(err) = err { - // Some error occured. In order to clear that error flag, you have to - // do a read from the sr register followed by a read from the dr - // register - // NOTE(read_volatile) see `write_volatile` below - unsafe { - ptr::read_volatile(&(*$USARTX::ptr()).sr as *const _ as *const _); - ptr::read_volatile(&(*$USARTX::ptr()).dr as *const _ as *const _); - } - Err(nb::Error::Other(err)) - } else { - // Check if a byte is available - if sr.rxne().bit_is_set() { - // Read the received byte - // NOTE(read_volatile) see `write_volatile` below - Ok(unsafe { - ptr::read_volatile(&(*$USARTX::ptr()).dr as *const _ as *const _) - }) - } else { - Err(nb::Error::WouldBlock) - } - } - } - - fn write(byte: u8) -> nb::Result<(), Infallible> { - // NOTE(unsafe) atomic read with no side effects - let sr = unsafe { (*$USARTX::ptr()).sr.read() }; - - if sr.txe().bit_is_set() { - // NOTE(unsafe) atomic write to stateless register - // NOTE(write_volatile) 8-bit write that's not possible through the svd2rust API - unsafe { - ptr::write_volatile(&(*$USARTX::ptr()).dr as *const _ as *mut _, byte) - } - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } - } - - fn flush() -> nb::Result<(), Infallible> { - // NOTE(unsafe) atomic read with no side effects - let sr = unsafe { (*$USARTX::ptr()).sr.read() }; - - if sr.tc().bit_is_set() { - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } - } - } - impl crate::hal::serial::Read for Rx<$USARTX> { type Error = Error; fn read(&mut self) -> nb::Result { - $USARTX::read() + unsafe { &*$USARTX::ptr() }.read() } } @@ -439,10 +429,10 @@ macro_rules! hal { type Error = Infallible; fn flush(&mut self) -> nb::Result<(), Self::Error> { - $USARTX::flush() + unsafe { &*$USARTX::ptr() }.flush() } fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { - $USARTX::write(byte) + unsafe { &*$USARTX::ptr() }.write(byte) } } @@ -450,7 +440,7 @@ macro_rules! hal { type Error = Error; fn read(&mut self) -> nb::Result { - $USARTX::read() + self.usart.deref().read() } } @@ -458,11 +448,11 @@ macro_rules! hal { type Error = Infallible; fn flush(&mut self) -> nb::Result<(), Self::Error> { - $USARTX::flush() + self.usart.deref().flush() } fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { - $USARTX::write(byte) + self.usart.deref().write(byte) } } From 6dfdef3d58e6d50b7029b070ed47be1fefd62e5c Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Fri, 17 Jan 2020 20:24:57 +0300 Subject: [PATCH 4/4] Move the trait definition below --- src/serial.rs | 138 +++++++++++++++++++++++++------------------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/src/serial.rs b/src/serial.rs index 4b13b075..da47f092 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -115,75 +115,6 @@ impl Pins for (PD8>, PD9>) { const REMAP: u8 = 0b11; } -/// Internal trait for the serial read / write logic. -trait UsartReadWrite: Deref { - fn read(&self) -> nb::Result { - let sr = self.sr.read(); - - // Check for any errors - let err = if sr.pe().bit_is_set() { - Some(Error::Parity) - } else if sr.fe().bit_is_set() { - Some(Error::Framing) - } else if sr.ne().bit_is_set() { - Some(Error::Noise) - } else if sr.ore().bit_is_set() { - Some(Error::Overrun) - } else { - None - }; - - if let Some(err) = err { - // Some error occured. In order to clear that error flag, you have to - // do a read from the sr register followed by a read from the dr - // register - // NOTE(read_volatile) see `write_volatile` below - unsafe { - ptr::read_volatile(&self.sr as *const _ as *const _); - ptr::read_volatile(&self.dr as *const _ as *const _); - } - Err(nb::Error::Other(err)) - } else { - // Check if a byte is available - if sr.rxne().bit_is_set() { - // Read the received byte - // NOTE(read_volatile) see `write_volatile` below - Ok(unsafe { - ptr::read_volatile(&self.dr as *const _ as *const _) - }) - } else { - Err(nb::Error::WouldBlock) - } - } - } - - fn write(&self, byte: u8) -> nb::Result<(), Infallible> { - let sr = self.sr.read(); - - if sr.txe().bit_is_set() { - // NOTE(unsafe) atomic write to stateless register - // NOTE(write_volatile) 8-bit write that's not possible through the svd2rust API - unsafe { - ptr::write_volatile(&self.dr as *const _ as *mut _, byte) - } - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } - } - - fn flush(&self) -> nb::Result<(), Infallible> { - let sr = self.sr.read(); - - if sr.tc().bit_is_set() { - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } - } -} -impl UsartReadWrite for &crate::stm32::usart1::RegisterBlock {} - pub enum Parity { ParityNone, ParityEven, @@ -261,6 +192,75 @@ pub struct Tx { _usart: PhantomData, } +/// Internal trait for the serial read / write logic. +trait UsartReadWrite: Deref { + fn read(&self) -> nb::Result { + let sr = self.sr.read(); + + // Check for any errors + let err = if sr.pe().bit_is_set() { + Some(Error::Parity) + } else if sr.fe().bit_is_set() { + Some(Error::Framing) + } else if sr.ne().bit_is_set() { + Some(Error::Noise) + } else if sr.ore().bit_is_set() { + Some(Error::Overrun) + } else { + None + }; + + if let Some(err) = err { + // Some error occured. In order to clear that error flag, you have to + // do a read from the sr register followed by a read from the dr + // register + // NOTE(read_volatile) see `write_volatile` below + unsafe { + ptr::read_volatile(&self.sr as *const _ as *const _); + ptr::read_volatile(&self.dr as *const _ as *const _); + } + Err(nb::Error::Other(err)) + } else { + // Check if a byte is available + if sr.rxne().bit_is_set() { + // Read the received byte + // NOTE(read_volatile) see `write_volatile` below + Ok(unsafe { + ptr::read_volatile(&self.dr as *const _ as *const _) + }) + } else { + Err(nb::Error::WouldBlock) + } + } + } + + fn write(&self, byte: u8) -> nb::Result<(), Infallible> { + let sr = self.sr.read(); + + if sr.txe().bit_is_set() { + // NOTE(unsafe) atomic write to stateless register + // NOTE(write_volatile) 8-bit write that's not possible through the svd2rust API + unsafe { + ptr::write_volatile(&self.dr as *const _ as *mut _, byte) + } + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } + + fn flush(&self) -> nb::Result<(), Infallible> { + let sr = self.sr.read(); + + if sr.tc().bit_is_set() { + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } +} +impl UsartReadWrite for &crate::stm32::usart1::RegisterBlock {} + macro_rules! hal { ($( $(#[$meta:meta])*