diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index 5aa26477..bf3d5d34 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -8,7 +8,9 @@ use core::fmt; use core::ops::Deref; use core::sync::atomic::{compiler_fence, Ordering::SeqCst}; +use embedded_hal::blocking::serial as bserial; use embedded_hal::digital::v2::OutputPin; +use embedded_hal::serial; #[cfg(any(feature = "52833", feature = "52840"))] use crate::pac::UARTE1; @@ -167,6 +169,10 @@ where /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub fn write(&mut self, tx_buffer: &[u8]) -> Result<(), Error> { + if tx_buffer.len() == 0 { + return Err(Error::TxBufferTooSmall); + } + if tx_buffer.len() > EASY_DMA_SIZE { return Err(Error::TxBufferTooLong); } @@ -174,39 +180,8 @@ where // We can only DMA out of RAM. slice_in_ram_or(tx_buffer, Error::BufferNotInRAM)?; - // Conservative compiler fence to prevent optimizations that do not - // take in to account actions by DMA. The fence has been placed here, - // before any DMA action has started. - compiler_fence(SeqCst); - - - // Set up the DMA write. - self.0.txd.ptr.write(|w| - // We're giving the register a pointer to the stack. Since we're - // waiting for the UARTE transaction to end before this stack pointer - // becomes invalid, there's nothing wrong here. - // - // The PTR field is a full 32 bits wide and accepts the full range - // of values. - unsafe { w.ptr().bits(tx_buffer.as_ptr() as u32) }); - self.0.txd.maxcnt.write(|w| - // We're giving it the length of the buffer, so no danger of - // accessing invalid memory. We have verified that the length of the - // buffer fits in an `u8`, so the cast to `u8` is also fine. - // - // The MAXCNT field is 8 bits wide and accepts the full range of - // values. - unsafe { w.maxcnt().bits(tx_buffer.len() as _) }); - - // Reset the event - self.0.events_endtx.reset(); - - // Start UARTE Transmit transaction. - self.0.tasks_starttx.write(|w| - // `1` is a valid value to write to task registers. - unsafe { w.bits(1) }); + start_write(&*self.0, tx_buffer); - // Wait for transmission to end. while self.0.events_endtx.read().bits() == 0 { // TODO: Do something here which uses less power. Like `wfi`. @@ -220,17 +195,7 @@ where // Reset the event self.0.events_txstopped.reset(); - // Lower power consumption by disabling the transmitter once we're - // finished. - self.0.tasks_stoptx.write(|w| - // `1` is a valid value to write to task registers. - unsafe { w.bits(1) }); - - // Wait for transmitter to stop. - while self.0.events_txstopped.read().bits() == 0 { - // Spin - } - + stop_write(&*self.0); Ok(()) } @@ -241,12 +206,12 @@ where /// /// The buffer must have a length of at most 255 bytes. pub fn read(&mut self, rx_buffer: &mut [u8]) -> Result<(), Error> { - self.start_read(rx_buffer)?; + start_read(&*self.0, rx_buffer)?; // Wait for transmission to end. while self.0.events_endrx.read().bits() == 0 {} - self.finalize_read(); + finalize_read(&*self.0); if self.0.rxd.amount.read().bits() != rx_buffer.len() as u32 { return Err(Error::Receive); @@ -279,7 +244,7 @@ where I: timer::Instance, { // Start the read. - self.start_read(rx_buffer)?; + start_read(&self.0, rx_buffer)?; // Start the timeout timer. timer.start(cycles); @@ -298,11 +263,11 @@ where if !event_complete { // Cancel the reception if it did not complete until now. - self.cancel_read(); + cancel_read(&self.0); } // Cleanup, even in the error case. - self.finalize_read(); + finalize_read(&self.0); let bytes_read = self.0.rxd.amount.read().bits() as usize; @@ -317,78 +282,6 @@ where Ok(()) } - /// Start a UARTE read transaction by setting the control - /// values and triggering a read task. - fn start_read(&mut self, rx_buffer: &mut [u8]) -> Result<(), Error> { - if rx_buffer.len() > EASY_DMA_SIZE { - return Err(Error::RxBufferTooLong); - } - - // NOTE: RAM slice check is not necessary, as a mutable slice can only be - // built from data located in RAM. - - // Conservative compiler fence to prevent optimizations that do not - // take in to account actions by DMA. The fence has been placed here, - // before any DMA action has started. - compiler_fence(SeqCst); - - // Set up the DMA read - self.0.rxd.ptr.write(|w| - // We're giving the register a pointer to the stack. Since we're - // waiting for the UARTE transaction to end before this stack pointer - // becomes invalid, there's nothing wrong here. - // - // The PTR field is a full 32 bits wide and accepts the full range - // of values. - unsafe { w.ptr().bits(rx_buffer.as_ptr() as u32) }); - self.0.rxd.maxcnt.write(|w| - // We're giving it the length of the buffer, so no danger of - // accessing invalid memory. We have verified that the length of the - // buffer fits in an `u8`, so the cast to `u8` is also fine. - // - // The MAXCNT field is at least 8 bits wide and accepts the full - // range of values. - unsafe { w.maxcnt().bits(rx_buffer.len() as _) }); - - // Start UARTE Receive transaction. - self.0.tasks_startrx.write(|w| - // `1` is a valid value to write to task registers. - unsafe { w.bits(1) }); - - Ok(()) - } - - /// Finalize a UARTE read transaction by clearing the event. - fn finalize_read(&mut self) { - // Reset the event, otherwise it will always read `1` from now on. - self.0.events_endrx.write(|w| w); - - // Conservative compiler fence to prevent optimizations that do not - // take in to account actions by DMA. The fence has been placed here, - // after all possible DMA actions have completed. - compiler_fence(SeqCst); - } - - /// Stop an unfinished UART read transaction and flush FIFO to DMA buffer. - fn cancel_read(&mut self) { - // Stop reception. - self.0.tasks_stoprx.write(|w| unsafe { w.bits(1) }); - - // Wait for the reception to have stopped. - while self.0.events_rxto.read().bits() == 0 {} - - // Reset the event flag. - self.0.events_rxto.write(|w| w); - - // Ask UART to flush FIFO to DMA buffer. - self.0.tasks_flushrx.write(|w| unsafe { w.bits(1) }); - - // Wait for the flush to complete. - while self.0.events_endrx.read().bits() == 0 {} - - // The event flag itself is later reset by `finalize_read`. - } - /// Return the raw interface to the underlying UARTE peripheral. pub fn free(self) -> (T, Pins) { let rxd = self.0.psel.rxd.read(); @@ -413,6 +306,142 @@ where }, ) } + + /// Split into implementations of embedded_hal::serial traits. + /// The size of the `tx_buf` slice passed to this method will determine + /// the size of the DMA transfers performed. + /// The `rx_buf` slice is an array of size 1 since the embedded_hal + /// traits only allow reading one byte at a time. + pub fn split( + self, + tx_buf: &'static mut [u8], + rx_buf: &'static mut [u8; 1], + ) -> Result<(UarteTx, UarteRx), Error> { + let tx = UarteTx::new(tx_buf)?; + let rx = UarteRx::new(rx_buf)?; + Ok((tx, rx)) + } +} + +/// Write via UARTE. +/// +/// This method uses transmits all bytes in `tx_buffer`. +fn start_write(uarte: &uarte0::RegisterBlock, tx_buffer: &[u8]) { + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // before any DMA action has started. + compiler_fence(SeqCst); + + // Reset the events. + uarte.events_endtx.reset(); + uarte.events_txstopped.reset(); + + // Set up the DMA write. + uarte.txd.ptr.write(|w| + // We're giving the register a pointer to the stack. Since we're + // waiting for the UARTE transaction to end before this stack pointer + // becomes invalid, there's nothing wrong here. + // + // The PTR field is a full 32 bits wide and accepts the full range + // of values. + unsafe { w.ptr().bits(tx_buffer.as_ptr() as u32) }); + uarte.txd.maxcnt.write(|w| + // We're giving it the length of the buffer, so no danger of + // accessing invalid memory. We have verified that the length of the + // buffer fits in an `u8`, so the cast to `u8` is also fine. + // + // The MAXCNT field is 8 bits wide and accepts the full range of + // values. + unsafe { w.maxcnt().bits(tx_buffer.len() as _) }); + + // Start UARTE Transmit transaction. + uarte.tasks_starttx.write(|w| + // `1` is a valid value to write to task registers. + unsafe { w.bits(1) }); +} + +fn stop_write(uarte: &uarte0::RegisterBlock) { + // `1` is a valid value to write to task registers. + uarte.tasks_stoptx.write(|w| unsafe { w.bits(1) }); + + // Wait for transmitter is stopped. + while uarte.events_txstopped.read().bits() == 0 {} +} + +/// Start a UARTE read transaction by setting the control +/// values and triggering a read task. +fn start_read(uarte: &uarte0::RegisterBlock, rx_buffer: &mut [u8]) -> Result<(), Error> { + if rx_buffer.len() == 0 { + return Err(Error::RxBufferTooSmall); + } + + if rx_buffer.len() > EASY_DMA_SIZE { + return Err(Error::RxBufferTooLong); + } + + // NOTE: RAM slice check is not necessary, as a mutable slice can only be + // built from data located in RAM. + + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // before any DMA action has started. + compiler_fence(SeqCst); + + // Set up the DMA read + uarte.rxd.ptr.write(|w| + // We're giving the register a pointer to the stack. Since we're + // waiting for the UARTE transaction to end before this stack pointer + // becomes invalid, there's nothing wrong here. + // + // The PTR field is a full 32 bits wide and accepts the full range + // of values. + unsafe { w.ptr().bits(rx_buffer.as_ptr() as u32) }); + uarte.rxd.maxcnt.write(|w| + // We're giving it the length of the buffer, so no danger of + // accessing invalid memory. We have verified that the length of the + // buffer fits in an `u8`, so the cast to `u8` is also fine. + // + // The MAXCNT field is at least 8 bits wide and accepts the full + // range of values. + unsafe { w.maxcnt().bits(rx_buffer.len() as _) }); + + // Start UARTE Receive transaction. + uarte.tasks_startrx.write(|w| + // `1` is a valid value to write to task registers. + unsafe { w.bits(1) }); + + Ok(()) +} + +/// Stop an unfinished UART read transaction and flush FIFO to DMA buffer. +fn cancel_read(uarte: &uarte0::RegisterBlock) { + // Stop reception. + uarte.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + + // Wait for the reception to have stopped. + while uarte.events_rxto.read().bits() == 0 {} + + // Reset the event flag. + uarte.events_rxto.write(|w| w); + + // Ask UART to flush FIFO to DMA buffer. + uarte.tasks_flushrx.write(|w| unsafe { w.bits(1) }); + + // Wait for the flush to complete. + while uarte.events_endrx.read().bits() == 0 {} + + // The event flag itself is later reset by `finalize_read`. +} + +/// Finalize a UARTE read transaction by clearing the event. +fn finalize_read(uarte: &uarte0::RegisterBlock) { + // Reset the event, otherwise it will always read `1` from now on. + uarte.events_endrx.write(|w| w); + + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // after all possible DMA actions have completed. + compiler_fence(SeqCst); } impl fmt::Write for Uarte @@ -441,6 +470,8 @@ pub struct Pins { #[derive(Debug)] pub enum Error { + TxBufferTooSmall, + RxBufferTooSmall, TxBufferTooLong, RxBufferTooLong, Transmit, @@ -449,18 +480,252 @@ pub enum Error { BufferNotInRAM, } -pub trait Instance: Deref + sealed::Sealed {} +pub trait Instance: Deref + sealed::Sealed { + fn ptr() -> *const uarte0::RegisterBlock; +} mod sealed { pub trait Sealed {} } impl sealed::Sealed for UARTE0 {} -impl Instance for UARTE0 {} +impl Instance for UARTE0 { + fn ptr() -> *const uarte0::RegisterBlock { + UARTE0::ptr() + } +} #[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] mod _uarte1 { use super::*; impl sealed::Sealed for UARTE1 {} - impl Instance for UARTE1 {} + impl Instance for UARTE1 { + fn ptr() -> *const uarte0::RegisterBlock { + UARTE1::ptr() + } + } +} + +/// Interface for the TX part of a UART instance that can be used independently of the RX part. +pub struct UarteTx +where + T: Instance, +{ + _marker: core::marker::PhantomData, + tx_buf: &'static mut [u8], + written: usize, +} + +/// Interface for the RX part of a UART instance that can be used independently of the TX part. +pub struct UarteRx +where + T: Instance, +{ + _marker: core::marker::PhantomData, + rx_buf: &'static mut [u8], +} + +impl UarteTx +where + T: Instance, +{ + fn new(tx_buf: &'static mut [u8]) -> Result, Error> { + if tx_buf.len() == 0 { + return Err(Error::TxBufferTooSmall); + } + + if tx_buf.len() > EASY_DMA_SIZE { + return Err(Error::TxBufferTooLong); + } + + Ok(UarteTx { + _marker: core::marker::PhantomData, + tx_buf, + written: 0, + }) + } +} + +impl UarteRx +where + T: Instance, +{ + fn new(rx_buf: &'static mut [u8]) -> Result, Error> { + if rx_buf.len() == 0 { + return Err(Error::RxBufferTooSmall); + } + + if rx_buf.len() > EASY_DMA_SIZE { + return Err(Error::RxBufferTooLong); + } + + Ok(UarteRx { + _marker: core::marker::PhantomData, + rx_buf, + }) + } +} + +impl Drop for UarteTx +where + T: Instance, +{ + fn drop(&mut self) { + let uarte = unsafe { &*T::ptr() }; + + let in_progress = uarte.events_txstarted.read().bits() == 1; + // Stop any ongoing transmission + if in_progress { + stop_write(uarte); + + // Reset events + uarte.events_endtx.reset(); + uarte.events_txstopped.reset(); + uarte.events_txstarted.reset(); + + // Ensure the above is done + compiler_fence(SeqCst); + } + } +} + +impl Drop for UarteRx +where + T: Instance, +{ + fn drop(&mut self) { + let uarte = unsafe { &*T::ptr() }; + + let in_progress = uarte.events_rxstarted.read().bits() == 1; + // Stop any ongoing reception + if in_progress { + cancel_read(uarte); + + // Reset events + uarte.events_endrx.reset(); + uarte.events_rxstarted.reset(); + + // Ensure the above is done + compiler_fence(SeqCst); + } + } +} + +impl serial::Write for UarteTx +where + T: Instance, +{ + type Error = Error; + + /// Write a single byte to the internal buffer. Returns nb::Error::WouldBlock if buffer is full. + fn write(&mut self, b: u8) -> nb::Result<(), Self::Error> { + let uarte = unsafe { &*T::ptr() }; + + // Prevent writing to buffer while DMA transfer is in progress. + if uarte.events_txstarted.read().bits() == 1 && uarte.events_endtx.read().bits() == 0 { + return Err(nb::Error::WouldBlock); + } + + if self.written >= self.tx_buf.len() { + self.flush()?; + } + + self.tx_buf[self.written] = b; + self.written += 1; + Ok(()) + } + + /// Flush the TX buffer non-blocking. Returns nb::Error::WouldBlock if not yet flushed. + fn flush(&mut self) -> nb::Result<(), Self::Error> { + let uarte = unsafe { &*T::ptr() }; + + // If txstarted is set, we are in the process of transmitting. + let in_progress = uarte.events_txstarted.read().bits() == 1; + + if in_progress { + let endtx = uarte.events_endtx.read().bits() != 0; + let txstopped = uarte.events_txstopped.read().bits() != 0; + if endtx || txstopped { + // We are done, cleanup the state. + uarte.events_txstarted.reset(); + self.written = 0; + + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // after all possible DMA actions have completed. + compiler_fence(SeqCst); + + if txstopped { + return Err(nb::Error::Other(Error::Transmit)); + } + + // Lower power consumption by disabling the transmitter once we're + // finished. + stop_write(uarte); + + compiler_fence(SeqCst); + Ok(()) + } else { + // Still not done, don't block. + Err(nb::Error::WouldBlock) + } + } else { + // No need to trigger transmit if we don't have anything written + if self.written == 0 { + return Ok(()); + } + + start_write(uarte, &self.tx_buf[0..self.written]); + + Err(nb::Error::WouldBlock) + } + } +} + +// Auto-implement the blocking variant +impl bserial::write::Default for UarteTx where T: Instance {} + +impl core::fmt::Write for UarteTx +where + T: Instance, +{ + fn write_str(&mut self, s: &str) -> fmt::Result { + s.as_bytes() + .iter() + .try_for_each(|c| nb::block!(self.write(*c))) + .map_err(|_| core::fmt::Error) + } +} + +impl serial::Read for UarteRx +where + T: Instance, +{ + type Error = Error; + fn read(&mut self) -> nb::Result { + let uarte = unsafe { &*T::ptr() }; + + compiler_fence(SeqCst); + + let in_progress = uarte.events_rxstarted.read().bits() == 1; + if in_progress && uarte.events_endrx.read().bits() == 0 { + return Err(nb::Error::WouldBlock); + } + + if in_progress { + uarte.events_rxstarted.reset(); + + finalize_read(uarte); + + if uarte.rxd.amount.read().bits() != 1 as u32 { + return Err(nb::Error::Other(Error::Receive)); + } + Ok(self.rx_buf[0]) + } else { + // We can only read 1 byte at a time, otherwise ENDTX might not be raised, + // causing the read to stall forever. + start_read(&uarte, self.rx_buf)?; + Err(nb::Error::WouldBlock) + } + } }