Skip to content

Commit 5da3e9b

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 ff3defe commit 5da3e9b

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
@@ -371,12 +371,12 @@ where
371371
)
372372
}
373373

374-
// Split into implementations of embedded_hal::serial traits. The buffers passed here must outlive any DMA transfers
374+
// 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.
375375
// that are initiated by the UartTx and UartRx.
376376
pub fn split<'a>(
377377
self,
378-
tx_buf: &'a mut [u8],
379-
rx_buf: &'a mut [u8],
378+
tx_buf: &'static mut [u8],
379+
rx_buf: &'static mut [u8],
380380
) -> Result<(UarteTx<'a, T>, UarteRx<'a, T>), Error> {
381381
let tx = UarteTx::new(tx_buf)?;
382382
let rx = UarteRx::new(rx_buf)?;
@@ -453,6 +453,7 @@ where
453453
{
454454
_marker: core::marker::PhantomData<T>,
455455
tx_buf: &'a mut [u8],
456+
written: u16,
456457
}
457458

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

@@ -487,14 +493,18 @@ where
487493
{
488494
fn new(rx_buf: &'a mut [u8]) -> Result<UarteRx<'a, T>, Error> {
489495
slice_in_ram_or(rx_buf, Error::BufferNotInRAM)?;
490-
if rx_buf.len() > 0 {
491-
Ok(UarteRx {
492-
_marker: core::marker::PhantomData,
493-
rx_buf,
494-
})
495-
} else {
496-
Err(Error::RxBufferTooSmall)
496+
if rx_buf.len() == 0 {
497+
return Err(Error::RxBufferTooSmall);
498+
}
499+
500+
if rx_buf.len() > EASY_DMA_SIZE {
501+
return Err(Error::RxBufferTooLong);
497502
}
503+
504+
Ok(UarteRx {
505+
_marker: core::marker::PhantomData,
506+
rx_buf,
507+
})
498508
}
499509
}
500510

@@ -557,8 +567,9 @@ where
557567

558568
pub mod serial {
559569

560-
///! Implementation of the embedded_hal::serial::* traits for UartTx and UartRx.
570+
///! Implementation of the embedded_hal::serial::* and embedded_hal::blocking::serial::* traits for UartTx and UartRx.
561571
use super::*;
572+
use embedded_hal::blocking::serial as bserial;
562573
use embedded_hal::serial;
563574
use nb;
564575

@@ -568,48 +579,21 @@ pub mod serial {
568579
{
569580
type Error = Error;
570581

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

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

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

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

669+
// Auto-implement the blocking variant
670+
impl<'a, T> bserial::write::Default<u8> for UarteTx<'a, T> where T: Instance {}
671+
652672
impl<'a, T> core::fmt::Write for UarteTx<'a, T>
653673
where
654674
T: Instance,

0 commit comments

Comments
 (0)