Skip to content

Commit fbe9d3e

Browse files
committed
Require DMA buffers to have static lifetime
* Allow making multi-byte DMA transfers by starting DMA only on flush(). Read is still limited by the embedded_hal trait, so will still read only 1 byte at a time. * Auto derive blocking trait from non-blocking version.
1 parent eb16b23 commit fbe9d3e

File tree

1 file changed

+78
-58
lines changed

1 file changed

+78
-58
lines changed

nrf-hal-common/src/uarte.rs

Lines changed: 78 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -373,12 +373,12 @@ where
373373
)
374374
}
375375

376-
// Split into implementations of embedded_hal::serial traits. The buffers passed here must outlive any DMA transfers
376+
// Split into implementations of embedded_hal::serial traits. The size of the slices passed to this method will determine the size of the DMA transfers performed.
377377
// that are initiated by the UartTx and UartRx.
378378
pub fn split<'a>(
379379
self,
380-
tx_buf: &'a mut [u8],
381-
rx_buf: &'a mut [u8],
380+
tx_buf: &'static mut [u8],
381+
rx_buf: &'static mut [u8],
382382
) -> Result<(UarteTx<'a, T>, UarteRx<'a, T>), Error> {
383383
let tx = UarteTx::new(tx_buf)?;
384384
let rx = UarteRx::new(rx_buf)?;
@@ -455,6 +455,7 @@ where
455455
{
456456
_marker: core::marker::PhantomData<T>,
457457
tx_buf: &'a mut [u8],
458+
written: u16,
458459
}
459460

460461
/// Interface for the RX part of a UART instance that can be used independently of the TX part.
@@ -472,14 +473,19 @@ where
472473
{
473474
fn new(tx_buf: &'a mut [u8]) -> Result<UarteTx<'a, T>, Error> {
474475
slice_in_ram_or(tx_buf, Error::BufferNotInRAM)?;
475-
if tx_buf.len() > 0 {
476-
Ok(UarteTx {
477-
_marker: core::marker::PhantomData,
478-
tx_buf,
479-
})
480-
} else {
481-
Err(Error::TxBufferTooSmall)
476+
if tx_buf.len() == 0 {
477+
return Err(Error::TxBufferTooSmall);
482478
}
479+
480+
if tx_buf.len() > EASY_DMA_SIZE {
481+
return Err(Error::TxBufferTooLong);
482+
}
483+
484+
Ok(UarteTx {
485+
_marker: core::marker::PhantomData,
486+
tx_buf,
487+
written: 0,
488+
})
483489
}
484490
}
485491

@@ -489,14 +495,18 @@ where
489495
{
490496
fn new(rx_buf: &'a mut [u8]) -> Result<UarteRx<'a, T>, Error> {
491497
slice_in_ram_or(rx_buf, Error::BufferNotInRAM)?;
492-
if rx_buf.len() > 0 {
493-
Ok(UarteRx {
494-
_marker: core::marker::PhantomData,
495-
rx_buf,
496-
})
497-
} else {
498-
Err(Error::RxBufferTooSmall)
498+
if rx_buf.len() == 0 {
499+
return Err(Error::RxBufferTooSmall);
500+
}
501+
502+
if rx_buf.len() > EASY_DMA_SIZE {
503+
return Err(Error::RxBufferTooLong);
499504
}
505+
506+
Ok(UarteRx {
507+
_marker: core::marker::PhantomData,
508+
rx_buf,
509+
})
500510
}
501511
}
502512

@@ -559,8 +569,9 @@ where
559569

560570
pub mod serial {
561571

562-
///! Implementation of the embedded_hal::serial::* traits for UartTx and UartRx.
572+
///! Implementation of the embedded_hal::serial::* and embedded_hal::blocking::serial::* traits for UartTx and UartRx.
563573
use super::*;
574+
use embedded_hal::blocking::serial as bserial;
564575
use embedded_hal::serial;
565576
use nb;
566577

@@ -570,48 +581,21 @@ pub mod serial {
570581
{
571582
type Error = Error;
572583

573-
/// Write a single byte non-blocking. Returns nb::Error::WouldBlock if not yet done.
584+
/// Write a single byte to the internal buffer. Returns nb::Error::WouldBlock if buffer is full.
574585
fn write(&mut self, b: u8) -> nb::Result<(), Self::Error> {
575586
let uarte = unsafe { &*T::ptr() };
576587

577-
// If txstarted is set, we are in the process of transmitting.
578-
let in_progress = uarte.events_txstarted.read().bits() == 1;
588+
// Prevent writing to buffer while DMA transfer is in progress.
589+
if uarte.events_txstarted.read().bits() == 1 {
590+
return Err(nb::Error::WouldBlock);
591+
}
579592

580-
if in_progress {
581-
self.flush()
593+
let written = self.written as usize;
594+
if written < self.tx_buf.len() {
595+
self.tx_buf[written] = b;
596+
self.written += 1;
597+
Ok(())
582598
} else {
583-
// Start a new transmission, copy value into transmit buffer.
584-
585-
self.tx_buf[0] = b;
586-
587-
// Conservative compiler fence to prevent optimizations that do not
588-
// take in to account actions by DMA. The fence has been placed here,
589-
// before any DMA action has started.
590-
compiler_fence(SeqCst);
591-
592-
// Reset the events.
593-
uarte.events_endtx.reset();
594-
uarte.events_txstopped.reset();
595-
596-
// Set up the DMA write.
597-
// We're giving the register a pointer to the tx buffer.
598-
//
599-
// The PTR field is a full 32 bits wide and accepts the full range
600-
// of values.
601-
uarte
602-
.txd
603-
.ptr
604-
.write(|w| unsafe { w.ptr().bits(self.tx_buf.as_ptr() as u32) });
605-
606-
// We're giving it a length of 1 to transmit 1 byte at a time.
607-
//
608-
// The MAXCNT field is 8 bits wide and accepts the full range of
609-
// values.
610-
uarte.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(1) });
611-
612-
// Start UARTE Transmit transaction.
613-
// `1` is a valid value to write to task registers.
614-
uarte.tasks_starttx.write(|w| unsafe { w.bits(1) });
615599
Err(nb::Error::WouldBlock)
616600
}
617601
}
@@ -620,10 +604,12 @@ pub mod serial {
620604
fn flush(&mut self) -> nb::Result<(), Self::Error> {
621605
let uarte = unsafe { &*T::ptr() };
622606

607+
// If txstarted is set, we are in the process of transmitting.
623608
let in_progress = uarte.events_txstarted.read().bits() == 1;
624-
let endtx = uarte.events_endtx.read().bits() != 0;
625-
let txstopped = uarte.events_txstopped.read().bits() != 0;
609+
626610
if in_progress {
611+
let endtx = uarte.events_endtx.read().bits() != 0;
612+
let txstopped = uarte.events_txstopped.read().bits() != 0;
627613
if endtx || txstopped {
628614
// We are done, cleanup the state.
629615
uarte.events_txstarted.reset();
@@ -646,11 +632,45 @@ pub mod serial {
646632
Err(nb::Error::WouldBlock)
647633
}
648634
} else {
649-
Ok(())
635+
// Conservative compiler fence to prevent optimizations that do not
636+
// take in to account actions by DMA. The fence has been placed here,
637+
// before any DMA action has started.
638+
compiler_fence(SeqCst);
639+
640+
// Reset the events.
641+
uarte.events_endtx.reset();
642+
uarte.events_txstopped.reset();
643+
644+
// Set up the DMA write.
645+
// We're giving the register a pointer to the tx buffer.
646+
//
647+
// The PTR field is a full 32 bits wide and accepts the full range
648+
// of values.
649+
uarte
650+
.txd
651+
.ptr
652+
.write(|w| unsafe { w.ptr().bits(self.tx_buf.as_ptr() as u32) });
653+
654+
// We're giving it a length of the number of bytes written to the buffer.
655+
//
656+
// The MAXCNT field is 8 bits wide and accepts the full range of
657+
// values.
658+
uarte
659+
.txd
660+
.maxcnt
661+
.write(|w| unsafe { w.maxcnt().bits(self.written) });
662+
663+
// Start UARTE Transmit transaction.
664+
// `1` is a valid value to write to task registers.
665+
uarte.tasks_starttx.write(|w| unsafe { w.bits(1) });
666+
Err(nb::Error::WouldBlock)
650667
}
651668
}
652669
}
653670

671+
// Auto-implement the blocking variant
672+
impl<'a, T> bserial::write::Default<u8> for UarteTx<'a, T> where T: Instance {}
673+
654674
impl<'a, T> core::fmt::Write for UarteTx<'a, T>
655675
where
656676
T: Instance,

0 commit comments

Comments
 (0)