From 8e0179d60c4ef0b5e36bd7f3f1a1e3842f1596c5 Mon Sep 17 00:00:00 2001 From: David Sawatzke Date: Sat, 5 Jan 2019 22:05:22 +0100 Subject: [PATCH 1/5] Add a tx only and a rx only serial instance --- src/serial.rs | 117 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 99 insertions(+), 18 deletions(-) diff --git a/src/serial.rs b/src/serial.rs index af7ebbd..a206d35 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -227,31 +227,66 @@ pub struct Tx { unsafe impl Send for Tx {} macro_rules! usart { - ($($USART:ident: ($usart:ident, $usartXen:ident, $apbenr:ident),)+) => { + ($($USART:ident: ($usart:ident, $usarttx:ident, $usartrx:ident, $usartXen:ident, $apbenr:ident),)+) => { $( use crate::stm32::$USART; - impl Serial<$USART, TXPIN, RXPIN> { + impl Serial<$USART, TXPIN, RXPIN> + where + TXPIN: TxPin<$USART>, + RXPIN: RxPin<$USART>, + { /// Creates a new serial instance pub fn $usart(usart: $USART, pins: (TXPIN, RXPIN), baud_rate: Bps, rcc: &mut Rcc) -> Self - where - TXPIN: TxPin<$USART>, - RXPIN: RxPin<$USART>, { + let mut serial = Serial { usart, pins }; + serial.enable(baud_rate, rcc); + serial + } + } + + impl Serial<$USART, TXPIN, ()> + where + TXPIN: TxPin<$USART>, + { + /// Creates a new tx-only serial instance + pub fn $usarttx(usart: $USART, txpin: TXPIN, baud_rate: Bps, rcc: &mut Rcc) -> Self + { + let rxpin = (); + let mut serial = Serial { usart, pins: (txpin, rxpin) }; + serial.enable(baud_rate, rcc); + serial + } + } + + impl Serial<$USART, (), RXPIN> + where + RXPIN: RxPin<$USART>, + { + /// Creates a new tx-only serial instance + pub fn $usartrx(usart: $USART, rxpin: RXPIN, baud_rate: Bps, rcc: &mut Rcc) -> Self + { + let txpin = (); + let mut serial = Serial { usart, pins: (txpin, rxpin) }; + serial.enable(baud_rate, rcc); + serial + } + } + + impl Serial<$USART, TXPIN, RXPIN> { + fn enable(&mut self, baud_rate: Bps, rcc: &mut Rcc) { // Enable clock for USART rcc.regs.$apbenr.modify(|_, w| w.$usartXen().set_bit()); // Calculate correct baudrate divisor on the fly let brr = rcc.clocks.pclk().0 / baud_rate.0; - usart.brr.write(|w| unsafe { w.bits(brr) }); + self.usart.brr.write(|w| unsafe { w.bits(brr) }); // Reset other registers to disable advanced USART features - usart.cr2.reset(); - usart.cr3.reset(); + self.usart.cr2.reset(); + self.usart.cr3.reset(); // Enable transmission and receiving - usart.cr1.modify(|_, w| w.te().set_bit().re().set_bit().ue().set_bit()); - - Serial { usart, pins } + self.usart.cr1.modify(|_, w| w.te().set_bit().re().set_bit().ue().set_bit()); } /// Starts listening for an interrupt event @@ -289,7 +324,7 @@ macro_rules! usart { } usart! { - USART1: (usart1, usart1en, apb2enr), + USART1: (usart1, usart1tx, usart1rx, usart1en, apb2enr), } #[cfg(any( feature = "stm32f030x8", @@ -302,7 +337,7 @@ usart! { feature = "stm32f091", ))] usart! { - USART2: (usart2, usart2en, apb1enr), + USART2: (usart2, usart2tx, usart2rx,usart2en, apb1enr), } #[cfg(any( feature = "stm32f030xc", @@ -312,13 +347,13 @@ usart! { feature = "stm32f091", ))] usart! { - USART3: (usart3, usart3en, apb1enr), - USART4: (usart4, usart4en, apb1enr), + USART3: (usart3, usart3tx, usart3rx,usart3en, apb1enr), + USART4: (usart4, usart4tx, usart4rx,usart4en, apb1enr), } #[cfg(any(feature = "stm32f030xc", feature = "stm32f091"))] usart! { - USART5: (usart5, usart5en, apb1enr), - USART6: (usart6, usart6en, apb2enr), + USART5: (usart5, usart5tx, usart5rx,usart5en, apb1enr), + USART6: (usart6, usart6tx, usart6rx,usart6en, apb2enr), } // It's s needed for the impls, but rustc doesn't recognize that @@ -353,6 +388,22 @@ where } } +impl embedded_hal::serial::Read for Serial +where + USART: Deref, + RXPIN: RxPin, +{ + type Error = Error; + + /// Tries to read a byte from the uart + fn read(&mut self) -> nb::Result { + Rx { + usart: &self.usart as *const _, + } + .read() + } +} + impl embedded_hal::serial::Write for Tx where USART: Deref, @@ -388,13 +439,42 @@ where } } +impl embedded_hal::serial::Write for Serial +where + USART: Deref, + TXPIN: TxPin, +{ + type Error = void::Void; + + /// Ensures that none of the previously written words are still buffered + fn flush(&mut self) -> nb::Result<(), Self::Error> { + Tx { + usart: &self.usart as *const _, + } + .flush() + } + + /// Tries to write a byte to the uart + /// Fails if the transmit buffer is full + fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { + Tx { + usart: &self.usart as *const _, + } + .write(byte) + } +} + impl Serial where USART: Deref, { /// Splits the UART Peripheral in a Tx and an Rx part /// This is required for sending/receiving - pub fn split(self) -> (Tx, Rx) { + pub fn split(self) -> (Tx, Rx) + where + TXPIN: TxPin, + RXPIN: RxPin, + { ( Tx { usart: &self.usart as *const _, @@ -404,6 +484,7 @@ where }, ) } + pub fn release(self) -> (USART, (TXPIN, RXPIN)) { (self.usart, self.pins) } From ee11a821a0756d3cdf0ea14592c0a6f8e151b476 Mon Sep 17 00:00:00 2001 From: David Sawatzke Date: Sat, 5 Jan 2019 23:14:30 +0100 Subject: [PATCH 2/5] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba25d5f..27b8d7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Support for stm32f0x1 line - @jessebraham - Support for HSE as a system clocksource (#25 - breaking change) - @zklapow +- Add ability to use a Tx/Rx only serial instance - @david-sawatzke ### Changed From 2c62bd99ebafb5c9a3b570ada100886400fd8989 Mon Sep 17 00:00:00 2001 From: David Sawatzke Date: Sun, 6 Jan 2019 12:38:25 +0100 Subject: [PATCH 3/5] Only enable usart tx/rx when needed --- src/serial.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/serial.rs b/src/serial.rs index a206d35..8a3591e 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -239,7 +239,9 @@ macro_rules! usart { pub fn $usart(usart: $USART, pins: (TXPIN, RXPIN), baud_rate: Bps, rcc: &mut Rcc) -> Self { let mut serial = Serial { usart, pins }; - serial.enable(baud_rate, rcc); + serial.configure(baud_rate, rcc); + // Enable transmission and receiving + serial.usart.cr1.modify(|_, w| w.te().set_bit().re().set_bit().ue().set_bit()); serial } } @@ -253,7 +255,9 @@ macro_rules! usart { { let rxpin = (); let mut serial = Serial { usart, pins: (txpin, rxpin) }; - serial.enable(baud_rate, rcc); + serial.configure(baud_rate, rcc); + // Enable transmission + serial.usart.cr1.modify(|_, w| w.te().set_bit().ue().set_bit()); serial } } @@ -267,13 +271,15 @@ macro_rules! usart { { let txpin = (); let mut serial = Serial { usart, pins: (txpin, rxpin) }; - serial.enable(baud_rate, rcc); + serial.configure(baud_rate, rcc); + // Enable receiving + serial.usart.cr1.modify(|_, w| w.re().set_bit().ue().set_bit()); serial } } impl Serial<$USART, TXPIN, RXPIN> { - fn enable(&mut self, baud_rate: Bps, rcc: &mut Rcc) { + fn configure(&mut self, baud_rate: Bps, rcc: &mut Rcc) { // Enable clock for USART rcc.regs.$apbenr.modify(|_, w| w.$usartXen().set_bit()); @@ -284,9 +290,6 @@ macro_rules! usart { // Reset other registers to disable advanced USART features self.usart.cr2.reset(); self.usart.cr3.reset(); - - // Enable transmission and receiving - self.usart.cr1.modify(|_, w| w.te().set_bit().re().set_bit().ue().set_bit()); } /// Starts listening for an interrupt event From ec1fa416e29b925e7dd468a591b9ca62913f6e8d Mon Sep 17 00:00:00 2001 From: David Sawatzke Date: Mon, 7 Jan 2019 19:07:46 +0100 Subject: [PATCH 4/5] Move `read`, `flush` & `write` in separate functions --- src/serial.rs | 126 +++++++++++++++++++++++++++----------------------- 1 file changed, 68 insertions(+), 58 deletions(-) diff --git a/src/serial.rs b/src/serial.rs index 8a3591e..da5d104 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -41,6 +41,8 @@ use embedded_hal::prelude::*; use crate::{gpio::*, rcc::Rcc, time::Bps}; +use core::marker::PhantomData; + /// Serial error #[derive(Debug)] pub enum Error { @@ -208,10 +210,13 @@ pub struct Serial { pins: (TXPIN, RXPIN), } +// Common register +type SerialRegisterBlock = crate::stm32::usart1::RegisterBlock; + /// Serial receiver pub struct Rx { - // This is ok, because the USART types only contains PhantomData - usart: *const USART, + usart: *const SerialRegisterBlock, + _instance: PhantomData, } // NOTE(unsafe) Required to allow protected shared access in handlers @@ -219,8 +224,8 @@ unsafe impl Send for Rx {} /// Serial transmitter pub struct Tx { - // This is ok, because the USART types only contains PhantomData - usart: *const USART, + usart: *const SerialRegisterBlock, + _instance: PhantomData, } // NOTE(unsafe) Required to allow protected shared access in handlers @@ -359,10 +364,6 @@ usart! { USART6: (usart6, usart6tx, usart6rx,usart6en, apb2enr), } -// It's s needed for the impls, but rustc doesn't recognize that -#[allow(dead_code)] -type SerialRegisterBlock = crate::stm32::usart1::RegisterBlock; - impl embedded_hal::serial::Read for Rx where USART: Deref, @@ -371,23 +372,7 @@ where /// Tries to read a byte from the uart fn read(&mut self) -> nb::Result { - // NOTE(unsafe) atomic read with no side effects - let isr = unsafe { (*self.usart).isr.read() }; - - Err(if isr.pe().bit_is_set() { - nb::Error::Other(Error::Parity) - } else if isr.fe().bit_is_set() { - nb::Error::Other(Error::Framing) - } else if isr.nf().bit_is_set() { - nb::Error::Other(Error::Noise) - } else if isr.ore().bit_is_set() { - nb::Error::Other(Error::Overrun) - } else if isr.rxne().bit_is_set() { - // NOTE(read_volatile) see `write_volatile` below - return Ok(unsafe { ptr::read_volatile(&(*self.usart).rdr as *const _ as *const _) }); - } else { - nb::Error::WouldBlock - }) + read(self.usart) } } @@ -400,10 +385,7 @@ where /// Tries to read a byte from the uart fn read(&mut self) -> nb::Result { - Rx { - usart: &self.usart as *const _, - } - .read() + read(&*self.usart) } } @@ -415,30 +397,13 @@ where /// Ensures that none of the previously written words are still buffered fn flush(&mut self) -> nb::Result<(), Self::Error> { - // NOTE(unsafe) atomic read with no side effects - let isr = unsafe { (*self.usart).isr.read() }; - - if isr.tc().bit_is_set() { - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } + flush(self.usart) } /// Tries to write a byte to the uart /// Fails if the transmit buffer is full fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { - // NOTE(unsafe) atomic read with no side effects - let isr = unsafe { (*self.usart).isr.read() }; - - if isr.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.usart).tdr as *const _ as *mut _, byte) } - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } + write(self.usart, byte) } } @@ -451,19 +416,13 @@ where /// Ensures that none of the previously written words are still buffered fn flush(&mut self) -> nb::Result<(), Self::Error> { - Tx { - usart: &self.usart as *const _, - } - .flush() + flush(&*self.usart) } /// Tries to write a byte to the uart /// Fails if the transmit buffer is full fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { - Tx { - usart: &self.usart as *const _, - } - .write(byte) + write(&*self.usart, byte) } } @@ -480,10 +439,12 @@ where { ( Tx { - usart: &self.usart as *const _, + usart: &*self.usart, + _instance: PhantomData, }, Rx { - usart: &self.usart as *const _, + usart: &*self.usart, + _instance: PhantomData, }, ) } @@ -504,3 +465,52 @@ where Ok(()) } } + +/// Ensures that none of the previously written words are still buffered +fn flush(usart: *const SerialRegisterBlock) -> nb::Result<(), void::Void> { + // NOTE(unsafe) atomic read with no side effects + let isr = unsafe { (*usart).isr.read() }; + + if isr.tc().bit_is_set() { + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } +} + +/// Tries to write a byte to the uart +/// Fails if the transmit buffer is full +fn write(usart: *const SerialRegisterBlock, byte: u8) -> nb::Result<(), void::Void> { + // NOTE(unsafe) atomic read with no side effects + let isr = unsafe { (*usart).isr.read() }; + + if isr.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(&(*usart).tdr as *const _ as *mut _, byte) } + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } +} + +/// Tries to read a byte from the uart +fn read(usart: *const SerialRegisterBlock) -> nb::Result { + // NOTE(unsafe) atomic read with no side effects + let isr = unsafe { (*usart).isr.read() }; + + Err(if isr.pe().bit_is_set() { + nb::Error::Other(Error::Parity) + } else if isr.fe().bit_is_set() { + nb::Error::Other(Error::Framing) + } else if isr.nf().bit_is_set() { + nb::Error::Other(Error::Noise) + } else if isr.ore().bit_is_set() { + nb::Error::Other(Error::Overrun) + } else if isr.rxne().bit_is_set() { + // NOTE(read_volatile) see `write_volatile` below + return Ok(unsafe { ptr::read_volatile(&(*usart).rdr as *const _ as *const _) }); + } else { + nb::Error::WouldBlock + }) +} From c8dd9b1f810ad516a0b42a0adc692ff394f3cce3 Mon Sep 17 00:00:00 2001 From: David Sawatzke Date: Fri, 11 Jan 2019 16:00:35 +0100 Subject: [PATCH 5/5] Add documentation and update a few examples Also add the `Write` implementation for Serial --- examples/serial_echo.rs | 8 +++--- examples/watchdog.rs | 10 +++----- src/serial.rs | 54 +++++++++++++++++++++++++++++++++++------ 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/examples/serial_echo.rs b/examples/serial_echo.rs index ec8163c..bed2a54 100644 --- a/examples/serial_echo.rs +++ b/examples/serial_echo.rs @@ -26,13 +26,11 @@ fn main() -> ! { let tx = gpioa.pa9.into_alternate_af1(cs); let rx = gpioa.pa10.into_alternate_af1(cs); - let serial = Serial::usart1(p.USART1, (tx, rx), 115_200.bps(), &mut rcc); - - let (mut tx, mut rx) = serial.split(); + let mut serial = Serial::usart1(p.USART1, (tx, rx), 115_200.bps(), &mut rcc); loop { - let received = block!(rx.read()).unwrap(); - block!(tx.write(received)).ok(); + let received = block!(serial.read()).unwrap(); + block!(serial.write(received)).ok(); } }); } diff --git a/examples/watchdog.rs b/examples/watchdog.rs index f639dcf..aa3400b 100644 --- a/examples/watchdog.rs +++ b/examples/watchdog.rs @@ -36,12 +36,10 @@ fn main() -> ! { let mut delay = Delay::new(cp.SYST, &rcc); let tx = gpioa.pa9.into_alternate_af1(cs); - let rx = gpioa.pa10.into_alternate_af1(cs); - let serial = Serial::usart1(p.USART1, (tx, rx), 115_200.bps(), &mut rcc); + let mut serial = Serial::usart1tx(p.USART1, tx, 115_200.bps(), &mut rcc); - let (mut tx, _rx) = serial.split(); - tx.write_str("RESET \r\n").ok(); + serial.write_str("RESET \r\n").ok(); watchdog.start(Hertz(1)); delay.delay_ms(500_u16); @@ -49,12 +47,12 @@ fn main() -> ! { delay.delay_ms(500_u16); watchdog.feed(); delay.delay_ms(500_u16); - tx.write_str("This will get printed \r\n").ok(); + serial.write_str("This will get printed \r\n").ok(); watchdog.feed(); // Now a reset happens while delaying delay.delay_ms(1500_u16); - tx.write_str("This won't\r\n").ok(); + serial.write_str("This won't\r\n").ok(); }); } diff --git a/src/serial.rs b/src/serial.rs index da5d104..7c620e9 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -1,8 +1,12 @@ //! API for the integrated USART ports //! -//! This only implements the usual asynchronous bidirectional 8-bit transfers, everything else is missing +//! This only implements the usual asynchronous bidirectional 8-bit transfers. +//! +//! It's possible to use a read-only/write-only serial implementation with +//! `usartXrx`/`usartXtx`. //! //! # Examples +//! Echo //! ``` no_run //! use stm32f0xx_hal as hal; //! @@ -20,13 +24,36 @@ //! let tx = gpioa.pa9.into_alternate_af1(cs); //! let rx = gpioa.pa10.into_alternate_af1(cs); //! -//! let serial = Serial::usart1(p.USART1, (tx, rx), 115_200.bps(), &mut rcc); +//! let mut serial = Serial::usart1(p.USART1, (tx, rx), 115_200.bps(), &mut rcc); +//! +//! loop { +//! let received = block!(serial.read()).unwrap(); +//! block!(serial.write(received)).ok(); +//! } +//! }); +//! ``` +//! +//! Hello World +//! ``` no_run +//! use stm32f0xx_hal as hal; +//! +//! use crate::hal::prelude::*; +//! use crate::hal::serial::Serial; +//! use crate::hal::stm32; +//! +//! use nb::block; +//! +//! cortex_m::interrupt::free(|cs| { +//! let rcc = p.RCC.configure().sysclk(48.mhz()).freeze(); +//! +//! let gpioa = p.GPIOA.split(&mut rcc); +//! +//! let tx = gpioa.pa9.into_alternate_af1(cs); //! -//! let (mut tx, mut rx) = serial.split(); +//! let mut serial = Serial::usart1tx(p.USART1, tx, 115_200.bps(), &mut rcc); //! //! loop { -//! let received = block!(rx.read()).unwrap(); -//! block!(tx.write(received)).ok(); +//! serial.write_str("Hello World!\r\n"); //! } //! }); //! ``` @@ -466,6 +493,19 @@ where } } +impl Write for Serial +where + USART: Deref, + TXPIN: TxPin, +{ + fn write_str(&mut self, s: &str) -> Result { + use nb::block; + + let _ = s.as_bytes().iter().map(|c| block!(self.write(*c))).last(); + Ok(()) + } +} + /// Ensures that none of the previously written words are still buffered fn flush(usart: *const SerialRegisterBlock) -> nb::Result<(), void::Void> { // NOTE(unsafe) atomic read with no side effects @@ -478,7 +518,7 @@ fn flush(usart: *const SerialRegisterBlock) -> nb::Result<(), void::Void> { } } -/// Tries to write a byte to the uart +/// Tries to write a byte to the UART /// Fails if the transmit buffer is full fn write(usart: *const SerialRegisterBlock, byte: u8) -> nb::Result<(), void::Void> { // NOTE(unsafe) atomic read with no side effects @@ -494,7 +534,7 @@ fn write(usart: *const SerialRegisterBlock, byte: u8) -> nb::Result<(), void::Vo } } -/// Tries to read a byte from the uart +/// Tries to read a byte from the UART fn read(usart: *const SerialRegisterBlock) -> nb::Result { // NOTE(unsafe) atomic read with no side effects let isr = unsafe { (*usart).isr.read() };