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 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 af7ebbd..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"); //! } //! }); //! ``` @@ -41,6 +68,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 +237,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,39 +251,77 @@ 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 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.configure(baud_rate, rcc); + // Enable transmission and receiving + serial.usart.cr1.modify(|_, w| w.te().set_bit().re().set_bit().ue().set_bit()); + 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.configure(baud_rate, rcc); + // Enable transmission + serial.usart.cr1.modify(|_, w| w.te().set_bit().ue().set_bit()); + 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.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 configure(&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(); - - // Enable transmission and receiving - usart.cr1.modify(|_, w| w.te().set_bit().re().set_bit().ue().set_bit()); - - Serial { usart, pins } + self.usart.cr2.reset(); + self.usart.cr3.reset(); } /// Starts listening for an interrupt event @@ -289,7 +359,7 @@ macro_rules! usart { } usart! { - USART1: (usart1, usart1en, apb2enr), + USART1: (usart1, usart1tx, usart1rx, usart1en, apb2enr), } #[cfg(any( feature = "stm32f030x8", @@ -302,7 +372,7 @@ usart! { feature = "stm32f091", ))] usart! { - USART2: (usart2, usart2en, apb1enr), + USART2: (usart2, usart2tx, usart2rx,usart2en, apb1enr), } #[cfg(any( feature = "stm32f030xc", @@ -312,19 +382,15 @@ 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 -#[allow(dead_code)] -type SerialRegisterBlock = crate::stm32::usart1::RegisterBlock; - impl embedded_hal::serial::Read for Rx where USART: Deref, @@ -333,23 +399,20 @@ 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) + } +} + +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 { + read(&*self.usart) } } @@ -361,30 +424,32 @@ 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) + } +} + +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> { + 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> { + write(&*self.usart, byte) } } @@ -394,16 +459,23 @@ where { /// 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 _, + usart: &*self.usart, + _instance: PhantomData, }, Rx { - usart: &self.usart as *const _, + usart: &*self.usart, + _instance: PhantomData, }, ) } + pub fn release(self) -> (USART, (TXPIN, RXPIN)) { (self.usart, self.pins) } @@ -420,3 +492,65 @@ where Ok(()) } } + +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 + 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 + }) +}