Skip to content

Commit 19e332c

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 aea8b09 commit 19e332c

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
@@ -370,6 +370,13 @@ where
370370
},
371371
)
372372
}
373+
374+
// Split into implementations of embedded_hal::serial traits
375+
pub fn split(self) -> (UarteTx<T>, UarteRx<T>) {
376+
let tx = UarteTx::new();
377+
let rx = UarteRx::new();
378+
(tx, rx)
379+
}
373380
}
374381

375382
impl<T> fmt::Write for Uarte<T>
@@ -406,18 +413,240 @@ pub enum Error {
406413
BufferNotInRAM,
407414
}
408415

409-
pub trait Instance: Deref<Target = uarte0::RegisterBlock> + sealed::Sealed {}
416+
pub trait Instance: Deref<Target = uarte0::RegisterBlock> + sealed::Sealed {
417+
fn ptr() -> *const uarte0::RegisterBlock;
418+
}
410419

411420
mod sealed {
412421
pub trait Sealed {}
413422
}
414423

415424
impl sealed::Sealed for UARTE0 {}
416-
impl Instance for UARTE0 {}
425+
impl Instance for UARTE0 {
426+
fn ptr() -> *const uarte0::RegisterBlock {
427+
UARTE0::ptr()
428+
}
429+
}
417430

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

0 commit comments

Comments
 (0)