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..da47f092 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(); @@ -39,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}; @@ -190,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])* @@ -350,44 +421,7 @@ macro_rules! hal { type Error = Error; fn read(&mut self) -> 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) - } - } + unsafe { &*$USARTX::ptr() }.read() } } @@ -395,32 +429,33 @@ macro_rules! hal { type Error = Infallible; fn flush(&mut self) -> nb::Result<(), Self::Error> { - // NOTE(unsafe) atomic read with no side effects - let sr = unsafe { (*$USARTX::ptr()).sr.read() }; + unsafe { &*$USARTX::ptr() }.flush() + } + fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { + unsafe { &*$USARTX::ptr() }.write(byte) + } + } - if sr.tc().bit_is_set() { - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } + impl crate::hal::serial::Read for Serial<$USARTX, PINS> { + type Error = Error; + + fn read(&mut self) -> nb::Result { + self.usart.deref().read() + } + } + + impl crate::hal::serial::Write for Serial<$USARTX, PINS> { + type Error = Infallible; + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.usart.deref().flush() } fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { - // 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) - } + self.usart.deref().write(byte) } } + )+ } }