Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Impl Read<u8> / Write<u8> for Serial #171

Merged
merged 4 commits into from
Jan 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>` / `Write<u8>` for `Serial` (#171)

## [v0.5.2] - 2019-12-15

Expand Down
21 changes: 14 additions & 7 deletions examples/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -68,22 +68,29 @@ 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);

// 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 {}
}
153 changes: 94 additions & 59 deletions src/serial.rs
Original file line number Diff line number Diff line change
@@ -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();
Expand Down Expand Up @@ -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};
Expand Down Expand Up @@ -190,6 +192,75 @@ pub struct Tx<USART> {
_usart: PhantomData<USART>,
}

/// Internal trait for the serial read / write logic.
trait UsartReadWrite: Deref<Target=crate::stm32::usart1::RegisterBlock> {
fn read(&self) -> nb::Result<u8, Error> {
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])*
Expand Down Expand Up @@ -350,77 +421,41 @@ macro_rules! hal {
type Error = Error;

fn read(&mut self) -> nb::Result<u8, Error> {
// 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()
dbrgn marked this conversation as resolved.
Show resolved Hide resolved
}
}

impl crate::hal::serial::Write<u8> for Tx<$USARTX> {
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<PINS> crate::hal::serial::Read<u8> for Serial<$USARTX, PINS> {
type Error = Error;

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

impl<PINS> crate::hal::serial::Write<u8> 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)
}
}

)+
}
}
Expand Down