Skip to content

Commit 64a00e5

Browse files
committed
Add embedded_hal::serial implementation for uarte
This adds a sub-module named "serial" that splits TX and RX parts of UART into separate types that implements the embedded_hal::serial interfaces. This allows using the nRF UART for drivers that rely on the embedded_hal::serial traits. The buffer management is primitive in order to support the semantics of the more restrictive embedded_hal traits.
1 parent 2755bf3 commit 64a00e5

File tree

1 file changed

+232
-3
lines changed

1 file changed

+232
-3
lines changed

nrf-hal-common/src/uarte.rs

Lines changed: 232 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,13 @@ where
372372
},
373373
)
374374
}
375+
376+
// Split into implementations of embedded_hal::serial traits
377+
pub fn split(self) -> (UarteTx<T>, UarteRx<T>) {
378+
let tx = UarteTx::new();
379+
let rx = UarteRx::new();
380+
(tx, rx)
381+
}
375382
}
376383

377384
impl<T> fmt::Write for Uarte<T>
@@ -408,18 +415,240 @@ pub enum Error {
408415
BufferNotInRAM,
409416
}
410417

411-
pub trait Instance: Deref<Target = uarte0::RegisterBlock> + sealed::Sealed {}
418+
pub trait Instance: Deref<Target = uarte0::RegisterBlock> + sealed::Sealed {
419+
fn ptr() -> *const uarte0::RegisterBlock;
420+
}
412421

413422
mod sealed {
414423
pub trait Sealed {}
415424
}
416425

417426
impl sealed::Sealed for UARTE0 {}
418-
impl Instance for UARTE0 {}
427+
impl Instance for UARTE0 {
428+
fn ptr() -> *const uarte0::RegisterBlock {
429+
UARTE0::ptr()
430+
}
431+
}
419432

420433
#[cfg(any(feature = "52833", feature = "52840", feature = "9160"))]
421434
mod _uarte1 {
422435
use super::*;
423436
impl sealed::Sealed for UARTE1 {}
424-
impl Instance for UARTE1 {}
437+
impl Instance for UARTE1 {
438+
fn ptr() -> *const uarte0::RegisterBlock {
439+
UARTE1::ptr()
440+
}
441+
}
442+
}
443+
444+
/// Interface for the TX part of a UART instance that can be used independently of the RX part.
445+
pub struct UarteTx<T> {
446+
_marker: core::marker::PhantomData<T>,
447+
tx_buf: [u8; 1],
448+
}
449+
450+
/// Interface for the RX part of a UART instance that can be used independently of the TX part.
451+
pub struct UarteRx<T> {
452+
_marker: core::marker::PhantomData<T>,
453+
rx_buf: [u8; 1],
454+
}
455+
456+
impl<T> UarteTx<T>
457+
where
458+
T: Instance,
459+
{
460+
fn new() -> UarteTx<T> {
461+
let tx = UarteTx {
462+
_marker: core::marker::PhantomData,
463+
tx_buf: [0; 1],
464+
};
465+
tx
466+
}
467+
}
468+
469+
impl<T> UarteRx<T>
470+
where
471+
T: Instance,
472+
{
473+
fn new() -> UarteRx<T> {
474+
let rx = UarteRx {
475+
_marker: core::marker::PhantomData,
476+
rx_buf: [0; 1],
477+
};
478+
rx
479+
}
480+
}
481+
482+
pub mod serial {
483+
484+
///! Implementation of the embedded_hal::serial::* traits for UartTx and UartRx.
485+
use super::*;
486+
use embedded_hal::serial;
487+
use nb;
488+
489+
impl<T> serial::Write<u8> for UarteTx<T>
490+
where
491+
T: Instance,
492+
{
493+
type Error = Error;
494+
495+
/// Write a single byte non-blocking. Returns nb::Error::WouldBlock if not yet done.
496+
fn write(&mut self, b: u8) -> nb::Result<(), Self::Error> {
497+
let uarte = unsafe { &*T::ptr() };
498+
499+
// If txstarted is set, we are in the process of transmitting.
500+
let in_progress = uarte.events_txstarted.read().bits() == 1;
501+
502+
if in_progress {
503+
self.flush()
504+
} else {
505+
// Start a new transmission, copy value into transmit buffer.
506+
507+
let tx_buffer = &mut self.tx_buf;
508+
tx_buffer[0] = b;
509+
510+
// Conservative compiler fence to prevent optimizations that do not
511+
// take in to account actions by DMA. The fence has been placed here,
512+
// before any DMA action has started.
513+
compiler_fence(SeqCst);
514+
515+
// Reset the events.
516+
uarte.events_endtx.reset();
517+
uarte.events_txstopped.reset();
518+
519+
// Set up the DMA write.
520+
// We're giving the register a pointer to the tx buffer.
521+
//
522+
// The PTR field is a full 32 bits wide and accepts the full range
523+
// of values.
524+
uarte
525+
.txd
526+
.ptr
527+
.write(|w| unsafe { w.ptr().bits(tx_buffer.as_ptr() as u32) });
528+
529+
// We're giving it the length of the buffer, so no danger of
530+
// accessing invalid memory. We have verified that the length of the
531+
// buffer fits in an `u8`, so the cast to `u8` is also fine.
532+
//
533+
// The MAXCNT field is 8 bits wide and accepts the full range of
534+
// values.
535+
uarte
536+
.txd
537+
.maxcnt
538+
.write(|w| unsafe { w.maxcnt().bits(tx_buffer.len() as _) });
539+
540+
// Start UARTE Transmit transaction.
541+
// `1` is a valid value to write to task registers.
542+
uarte.tasks_starttx.write(|w| unsafe { w.bits(1) });
543+
Err(nb::Error::WouldBlock)
544+
}
545+
}
546+
547+
/// Flush the TX buffer non-blocking. Returns nb::Error::WouldBlock if not yet flushed.
548+
fn flush(&mut self) -> nb::Result<(), Self::Error> {
549+
let uarte = unsafe { &*T::ptr() };
550+
551+
let in_progress = uarte.events_txstarted.read().bits() == 1;
552+
let endtx = uarte.events_endtx.read().bits() != 0;
553+
let txstopped = uarte.events_txstopped.read().bits() != 0;
554+
if in_progress {
555+
if endtx || txstopped {
556+
// We are done, cleanup the state.
557+
uarte.events_txstarted.reset();
558+
// Conservative compiler fence to prevent optimizations that do not
559+
// take in to account actions by DMA. The fence has been placed here,
560+
// after all possible DMA actions have completed.
561+
compiler_fence(SeqCst);
562+
563+
if txstopped {
564+
return Err(nb::Error::Other(Error::Transmit));
565+
}
566+
567+
// Lower power consumption by disabling the transmitter once we're
568+
// finished.
569+
// `1` is a valid value to write to task registers.
570+
uarte.tasks_stoptx.write(|w| unsafe { w.bits(1) });
571+
Ok(())
572+
} else {
573+
// Still not done, don't block.
574+
Err(nb::Error::WouldBlock)
575+
}
576+
} else {
577+
Ok(())
578+
}
579+
}
580+
}
581+
582+
impl<T> core::fmt::Write for UarteTx<T>
583+
where
584+
T: Instance,
585+
{
586+
fn write_str(&mut self, s: &str) -> fmt::Result {
587+
s.as_bytes()
588+
.iter()
589+
.try_for_each(|c| nb::block!(self.write(*c)))
590+
.map_err(|_| core::fmt::Error)
591+
}
592+
}
593+
594+
impl<T> serial::Read<u8> for UarteRx<T>
595+
where
596+
T: Instance,
597+
{
598+
type Error = Error;
599+
fn read(&mut self) -> nb::Result<u8, Self::Error> {
600+
let uarte = unsafe { &*T::ptr() };
601+
602+
compiler_fence(SeqCst);
603+
604+
let in_progress = uarte.events_rxstarted.read().bits() == 1;
605+
if in_progress && uarte.events_endrx.read().bits() == 0 {
606+
return Err(nb::Error::WouldBlock);
607+
}
608+
609+
if in_progress {
610+
let b = self.rx_buf[0];
611+
uarte.events_rxstarted.write(|w| w);
612+
uarte.events_endrx.write(|w| w);
613+
614+
compiler_fence(SeqCst);
615+
if uarte.rxd.amount.read().bits() != 1 as u32 {
616+
return Err(nb::Error::Other(Error::Receive));
617+
}
618+
Ok(b)
619+
} else {
620+
let rx_buf = &mut self.rx_buf;
621+
622+
// We're giving the register a pointer to the rx buffer.
623+
//
624+
// The PTR field is a full 32 bits wide and accepts the full range
625+
// of values.
626+
uarte
627+
.rxd
628+
.ptr
629+
.write(|w| unsafe { w.ptr().bits(rx_buf.as_ptr() as u32) });
630+
631+
// We're giving it the length of the buffer, so no danger of
632+
// accessing invalid memory.
633+
//
634+
// The MAXCNT field is at least 8 bits wide and accepts the full
635+
// range of values.
636+
uarte
637+
.rxd
638+
.maxcnt
639+
.write(|w| unsafe { w.maxcnt().bits(rx_buf.len() as _) });
640+
641+
// Start UARTE Receive transaction.
642+
// `1` is a valid value to write to task registers.
643+
uarte.tasks_startrx.write(|w| unsafe { w.bits(1) });
644+
// Conservative compiler fence to prevent optimizations that do not
645+
// take in to account actions by DMA. The fence has been placed here,
646+
// after all possible DMA actions have completed.
647+
648+
compiler_fence(SeqCst);
649+
650+
Err(nb::Error::WouldBlock)
651+
}
652+
}
653+
}
425654
}

0 commit comments

Comments
 (0)