Merge pull request #1208 from embassy-rs/nrf-uarte-lockfree
nrf/buffered_uarte: make it work without rts/cts, and lock-free.
This commit is contained in:
		
							
								
								
									
										1
									
								
								ci.sh
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								ci.sh
									
									
									
									
									
								
							@@ -133,6 +133,7 @@ cargo batch  \
 | 
			
		||||
    --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \
 | 
			
		||||
    --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \
 | 
			
		||||
    --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
 | 
			
		||||
    --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
 | 
			
		||||
    $BUILD_EXTRA
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,5 @@
 | 
			
		||||
//! Async buffered UART driver.
 | 
			
		||||
//!
 | 
			
		||||
//! WARNING!!! The functionality provided here is intended to be used only
 | 
			
		||||
//! in situations where hardware flow control are available i.e. CTS and RTS.
 | 
			
		||||
//! This is a problem that should be addressed at a later stage and can be
 | 
			
		||||
//! fully explained at <https://github.com/embassy-rs/embassy/issues/536>.
 | 
			
		||||
//!
 | 
			
		||||
//! Note that discarding a future from a read or write operation may lead to losing
 | 
			
		||||
//! data. For example, when using `futures_util::future::select` and completion occurs
 | 
			
		||||
//! on the "other" future, you should capture the incomplete future and continue to use
 | 
			
		||||
@@ -13,82 +8,128 @@
 | 
			
		||||
//!
 | 
			
		||||
//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used.
 | 
			
		||||
 | 
			
		||||
use core::cell::RefCell;
 | 
			
		||||
use core::cmp::min;
 | 
			
		||||
use core::future::poll_fn;
 | 
			
		||||
use core::sync::atomic::{compiler_fence, Ordering};
 | 
			
		||||
use core::slice;
 | 
			
		||||
use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering};
 | 
			
		||||
use core::task::Poll;
 | 
			
		||||
 | 
			
		||||
use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
 | 
			
		||||
use embassy_hal_common::ring_buffer::RingBuffer;
 | 
			
		||||
use embassy_cortex_m::interrupt::Interrupt;
 | 
			
		||||
use embassy_hal_common::atomic_ring_buffer::RingBuffer;
 | 
			
		||||
use embassy_hal_common::{into_ref, PeripheralRef};
 | 
			
		||||
use embassy_sync::waitqueue::WakerRegistration;
 | 
			
		||||
use embassy_sync::waitqueue::AtomicWaker;
 | 
			
		||||
// Re-export SVD variants to allow user to directly set values
 | 
			
		||||
pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
 | 
			
		||||
 | 
			
		||||
use crate::gpio::{self, Pin as GpioPin};
 | 
			
		||||
use crate::gpio::sealed::Pin;
 | 
			
		||||
use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
 | 
			
		||||
use crate::interrupt::InterruptExt;
 | 
			
		||||
use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};
 | 
			
		||||
use crate::timer::{Frequency, Instance as TimerInstance, Timer};
 | 
			
		||||
use crate::ppi::{
 | 
			
		||||
    self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task,
 | 
			
		||||
};
 | 
			
		||||
use crate::timer::{Instance as TimerInstance, Timer};
 | 
			
		||||
use crate::uarte::{apply_workaround_for_enable_anomaly, Config, Instance as UarteInstance};
 | 
			
		||||
use crate::{pac, Peripheral};
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug, PartialEq)]
 | 
			
		||||
enum RxState {
 | 
			
		||||
    Idle,
 | 
			
		||||
    Receiving,
 | 
			
		||||
}
 | 
			
		||||
mod sealed {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug, PartialEq)]
 | 
			
		||||
enum TxState {
 | 
			
		||||
    Idle,
 | 
			
		||||
    Transmitting(usize),
 | 
			
		||||
}
 | 
			
		||||
    pub struct State {
 | 
			
		||||
        pub tx_waker: AtomicWaker,
 | 
			
		||||
        pub tx_buf: RingBuffer,
 | 
			
		||||
        pub tx_count: AtomicUsize,
 | 
			
		||||
 | 
			
		||||
/// A type for storing the state of the UARTE peripheral that can be stored in a static.
 | 
			
		||||
pub struct State<'d, U: UarteInstance, T: TimerInstance>(StateStorage<StateInner<'d, U, T>>);
 | 
			
		||||
impl<'d, U: UarteInstance, T: TimerInstance> State<'d, U, T> {
 | 
			
		||||
    /// Create an instance for storing UARTE peripheral state.
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Self(StateStorage::new())
 | 
			
		||||
        pub rx_waker: AtomicWaker,
 | 
			
		||||
        pub rx_buf: RingBuffer,
 | 
			
		||||
        pub rx_bufs: AtomicU8,
 | 
			
		||||
        pub rx_ppi_ch: AtomicU8,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct StateInner<'d, U: UarteInstance, T: TimerInstance> {
 | 
			
		||||
    _peri: PeripheralRef<'d, U>,
 | 
			
		||||
    timer: Timer<'d, T>,
 | 
			
		||||
    _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>,
 | 
			
		||||
    _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>,
 | 
			
		||||
/// UART error.
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[non_exhaustive]
 | 
			
		||||
pub enum Error {
 | 
			
		||||
    // No errors for now
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    rx: RingBuffer<'d>,
 | 
			
		||||
    rx_state: RxState,
 | 
			
		||||
    rx_waker: WakerRegistration,
 | 
			
		||||
pub(crate) use sealed::State;
 | 
			
		||||
 | 
			
		||||
    tx: RingBuffer<'d>,
 | 
			
		||||
    tx_state: TxState,
 | 
			
		||||
    tx_waker: WakerRegistration,
 | 
			
		||||
impl State {
 | 
			
		||||
    pub(crate) const fn new() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            tx_waker: AtomicWaker::new(),
 | 
			
		||||
            tx_buf: RingBuffer::new(),
 | 
			
		||||
            tx_count: AtomicUsize::new(0),
 | 
			
		||||
 | 
			
		||||
            rx_waker: AtomicWaker::new(),
 | 
			
		||||
            rx_buf: RingBuffer::new(),
 | 
			
		||||
            rx_bufs: AtomicU8::new(0),
 | 
			
		||||
            rx_ppi_ch: AtomicU8::new(0),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Buffered UARTE driver.
 | 
			
		||||
pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> {
 | 
			
		||||
    inner: RefCell<PeripheralMutex<'d, StateInner<'d, U, T>>>,
 | 
			
		||||
    _peri: PeripheralRef<'d, U>,
 | 
			
		||||
    timer: Timer<'d, T>,
 | 
			
		||||
    _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>,
 | 
			
		||||
    _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>,
 | 
			
		||||
    _ppi_group: PpiGroup<'d, AnyGroup>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {}
 | 
			
		||||
 | 
			
		||||
impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
 | 
			
		||||
    /// Create a new instance of a BufferedUarte.
 | 
			
		||||
    /// Create a new BufferedUarte without hardware flow control.
 | 
			
		||||
    ///
 | 
			
		||||
    /// See the [module documentation](crate::buffered_uarte) for more details about the intended use.
 | 
			
		||||
    /// # Panics
 | 
			
		||||
    ///
 | 
			
		||||
    /// The BufferedUarte uses the provided state to store the buffers and peripheral state. The timer and ppi channels are used to 'emulate' idle line detection so that read operations
 | 
			
		||||
    /// can return early if there is no data to receive.
 | 
			
		||||
    /// Panics if `rx_buffer.len()` is odd.
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        state: &'d mut State<'d, U, T>,
 | 
			
		||||
        peri: impl Peripheral<P = U> + 'd,
 | 
			
		||||
        uarte: impl Peripheral<P = U> + 'd,
 | 
			
		||||
        timer: impl Peripheral<P = T> + 'd,
 | 
			
		||||
        ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
 | 
			
		||||
        ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
 | 
			
		||||
        ppi_ch1: impl Peripheral<P = impl ConfigurableChannel> + 'd,
 | 
			
		||||
        ppi_ch2: impl Peripheral<P = impl ConfigurableChannel> + 'd,
 | 
			
		||||
        ppi_group: impl Peripheral<P = impl Group> + 'd,
 | 
			
		||||
        irq: impl Peripheral<P = U::Interrupt> + 'd,
 | 
			
		||||
        rxd: impl Peripheral<P = impl GpioPin> + 'd,
 | 
			
		||||
        txd: impl Peripheral<P = impl GpioPin> + 'd,
 | 
			
		||||
        config: Config,
 | 
			
		||||
        rx_buffer: &'d mut [u8],
 | 
			
		||||
        tx_buffer: &'d mut [u8],
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        into_ref!(rxd, txd, ppi_ch1, ppi_ch2, ppi_group);
 | 
			
		||||
        Self::new_inner(
 | 
			
		||||
            uarte,
 | 
			
		||||
            timer,
 | 
			
		||||
            ppi_ch1.map_into(),
 | 
			
		||||
            ppi_ch2.map_into(),
 | 
			
		||||
            ppi_group.map_into(),
 | 
			
		||||
            irq,
 | 
			
		||||
            rxd.map_into(),
 | 
			
		||||
            txd.map_into(),
 | 
			
		||||
            None,
 | 
			
		||||
            None,
 | 
			
		||||
            config,
 | 
			
		||||
            rx_buffer,
 | 
			
		||||
            tx_buffer,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a new BufferedUarte with hardware flow control (RTS/CTS)
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Panics
 | 
			
		||||
    ///
 | 
			
		||||
    /// Panics if `rx_buffer.len()` is odd.
 | 
			
		||||
    pub fn new_with_rtscts(
 | 
			
		||||
        uarte: impl Peripheral<P = U> + 'd,
 | 
			
		||||
        timer: impl Peripheral<P = T> + 'd,
 | 
			
		||||
        ppi_ch1: impl Peripheral<P = impl ConfigurableChannel> + 'd,
 | 
			
		||||
        ppi_ch2: impl Peripheral<P = impl ConfigurableChannel> + 'd,
 | 
			
		||||
        ppi_group: impl Peripheral<P = impl Group> + 'd,
 | 
			
		||||
        irq: impl Peripheral<P = U::Interrupt> + 'd,
 | 
			
		||||
        rxd: impl Peripheral<P = impl GpioPin> + 'd,
 | 
			
		||||
        txd: impl Peripheral<P = impl GpioPin> + 'd,
 | 
			
		||||
@@ -98,12 +139,45 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
 | 
			
		||||
        rx_buffer: &'d mut [u8],
 | 
			
		||||
        tx_buffer: &'d mut [u8],
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        into_ref!(peri, ppi_ch1, ppi_ch2, irq, rxd, txd, cts, rts);
 | 
			
		||||
        into_ref!(rxd, txd, cts, rts, ppi_ch1, ppi_ch2, ppi_group);
 | 
			
		||||
        Self::new_inner(
 | 
			
		||||
            uarte,
 | 
			
		||||
            timer,
 | 
			
		||||
            ppi_ch1.map_into(),
 | 
			
		||||
            ppi_ch2.map_into(),
 | 
			
		||||
            ppi_group.map_into(),
 | 
			
		||||
            irq,
 | 
			
		||||
            rxd.map_into(),
 | 
			
		||||
            txd.map_into(),
 | 
			
		||||
            Some(cts.map_into()),
 | 
			
		||||
            Some(rts.map_into()),
 | 
			
		||||
            config,
 | 
			
		||||
            rx_buffer,
 | 
			
		||||
            tx_buffer,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn new_inner(
 | 
			
		||||
        peri: impl Peripheral<P = U> + 'd,
 | 
			
		||||
        timer: impl Peripheral<P = T> + 'd,
 | 
			
		||||
        ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>,
 | 
			
		||||
        ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>,
 | 
			
		||||
        ppi_group: PeripheralRef<'d, AnyGroup>,
 | 
			
		||||
        irq: impl Peripheral<P = U::Interrupt> + 'd,
 | 
			
		||||
        rxd: PeripheralRef<'d, AnyPin>,
 | 
			
		||||
        txd: PeripheralRef<'d, AnyPin>,
 | 
			
		||||
        cts: Option<PeripheralRef<'d, AnyPin>>,
 | 
			
		||||
        rts: Option<PeripheralRef<'d, AnyPin>>,
 | 
			
		||||
        config: Config,
 | 
			
		||||
        rx_buffer: &'d mut [u8],
 | 
			
		||||
        tx_buffer: &'d mut [u8],
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        into_ref!(peri, timer, irq);
 | 
			
		||||
 | 
			
		||||
        assert!(rx_buffer.len() % 2 == 0);
 | 
			
		||||
 | 
			
		||||
        let r = U::regs();
 | 
			
		||||
 | 
			
		||||
        let mut timer = Timer::new(timer);
 | 
			
		||||
 | 
			
		||||
        rxd.conf().write(|w| w.input().connect().drive().h0h1());
 | 
			
		||||
        r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
 | 
			
		||||
 | 
			
		||||
@@ -111,92 +185,200 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
 | 
			
		||||
        txd.conf().write(|w| w.dir().output().drive().h0h1());
 | 
			
		||||
        r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) });
 | 
			
		||||
 | 
			
		||||
        cts.conf().write(|w| w.input().connect().drive().h0h1());
 | 
			
		||||
        if let Some(pin) = &cts {
 | 
			
		||||
            pin.conf().write(|w| w.input().connect().drive().h0h1());
 | 
			
		||||
        }
 | 
			
		||||
        r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) });
 | 
			
		||||
 | 
			
		||||
        rts.set_high();
 | 
			
		||||
        rts.conf().write(|w| w.dir().output().drive().h0h1());
 | 
			
		||||
        if let Some(pin) = &rts {
 | 
			
		||||
            pin.set_high();
 | 
			
		||||
            pin.conf().write(|w| w.dir().output().drive().h0h1());
 | 
			
		||||
        }
 | 
			
		||||
        r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) });
 | 
			
		||||
 | 
			
		||||
        r.baudrate.write(|w| w.baudrate().variant(config.baudrate));
 | 
			
		||||
        r.config.write(|w| w.parity().variant(config.parity));
 | 
			
		||||
        // Initialize state
 | 
			
		||||
        let s = U::buffered_state();
 | 
			
		||||
        s.tx_count.store(0, Ordering::Relaxed);
 | 
			
		||||
        s.rx_bufs.store(0, Ordering::Relaxed);
 | 
			
		||||
        let len = tx_buffer.len();
 | 
			
		||||
        unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
 | 
			
		||||
        let len = rx_buffer.len();
 | 
			
		||||
        unsafe { s.rx_buf.init(rx_buffer.as_mut_ptr(), len) };
 | 
			
		||||
 | 
			
		||||
        // Configure
 | 
			
		||||
        r.config.write(|w| {
 | 
			
		||||
            w.hwfc().bit(true);
 | 
			
		||||
            w.hwfc().bit(false);
 | 
			
		||||
            w.parity().variant(config.parity);
 | 
			
		||||
            w
 | 
			
		||||
        });
 | 
			
		||||
        r.baudrate.write(|w| w.baudrate().variant(config.baudrate));
 | 
			
		||||
 | 
			
		||||
        // Enable interrupts
 | 
			
		||||
        r.intenset.write(|w| w.endrx().set().endtx().set());
 | 
			
		||||
        // clear errors
 | 
			
		||||
        let errors = r.errorsrc.read().bits();
 | 
			
		||||
        r.errorsrc.write(|w| unsafe { w.bits(errors) });
 | 
			
		||||
 | 
			
		||||
        // Disable the irq, let the Registration enable it when everything is set up.
 | 
			
		||||
        irq.disable();
 | 
			
		||||
        irq.pend();
 | 
			
		||||
        r.events_rxstarted.reset();
 | 
			
		||||
        r.events_txstarted.reset();
 | 
			
		||||
        r.events_error.reset();
 | 
			
		||||
        r.events_endrx.reset();
 | 
			
		||||
        r.events_endtx.reset();
 | 
			
		||||
 | 
			
		||||
        // Enable interrupts
 | 
			
		||||
        r.intenclr.write(|w| unsafe { w.bits(!0) });
 | 
			
		||||
        r.intenset.write(|w| {
 | 
			
		||||
            w.endtx().set();
 | 
			
		||||
            w.rxstarted().set();
 | 
			
		||||
            w.error().set();
 | 
			
		||||
            w
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Enable UARTE instance
 | 
			
		||||
        apply_workaround_for_enable_anomaly(&r);
 | 
			
		||||
        r.enable.write(|w| w.enable().enabled());
 | 
			
		||||
 | 
			
		||||
        // BAUDRATE register values are `baudrate * 2^32 / 16000000`
 | 
			
		||||
        // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values
 | 
			
		||||
        //
 | 
			
		||||
        // We want to stop RX if line is idle for 2 bytes worth of time
 | 
			
		||||
        // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit)
 | 
			
		||||
        // This gives us the amount of 16M ticks for 20 bits.
 | 
			
		||||
        let timeout = 0x8000_0000 / (config.baudrate as u32 / 40);
 | 
			
		||||
        // Configure byte counter.
 | 
			
		||||
        let mut timer = Timer::new_counter(timer);
 | 
			
		||||
        timer.cc(1).write(rx_buffer.len() as u32 * 2);
 | 
			
		||||
        timer.cc(1).short_compare_clear();
 | 
			
		||||
        timer.clear();
 | 
			
		||||
        timer.start();
 | 
			
		||||
 | 
			
		||||
        timer.set_frequency(Frequency::F16MHz);
 | 
			
		||||
        timer.cc(0).write(timeout);
 | 
			
		||||
        timer.cc(0).short_compare_clear();
 | 
			
		||||
        timer.cc(0).short_compare_stop();
 | 
			
		||||
 | 
			
		||||
        let mut ppi_ch1 = Ppi::new_one_to_two(
 | 
			
		||||
            ppi_ch1.map_into(),
 | 
			
		||||
            Event::from_reg(&r.events_rxdrdy),
 | 
			
		||||
            timer.task_clear(),
 | 
			
		||||
            timer.task_start(),
 | 
			
		||||
        );
 | 
			
		||||
        let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_rxdrdy), timer.task_count());
 | 
			
		||||
        ppi_ch1.enable();
 | 
			
		||||
 | 
			
		||||
        let mut ppi_ch2 = Ppi::new_one_to_one(
 | 
			
		||||
            ppi_ch2.map_into(),
 | 
			
		||||
            timer.cc(0).event_compare(),
 | 
			
		||||
            Task::from_reg(&r.tasks_stoprx),
 | 
			
		||||
        s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed);
 | 
			
		||||
        let mut ppi_group = PpiGroup::new(ppi_group);
 | 
			
		||||
        let mut ppi_ch2 = Ppi::new_one_to_two(
 | 
			
		||||
            ppi_ch2,
 | 
			
		||||
            Event::from_reg(&r.events_endrx),
 | 
			
		||||
            Task::from_reg(&r.tasks_startrx),
 | 
			
		||||
            ppi_group.task_disable_all(),
 | 
			
		||||
        );
 | 
			
		||||
        ppi_ch2.enable();
 | 
			
		||||
        ppi_ch2.disable();
 | 
			
		||||
        ppi_group.add_channel(&ppi_ch2);
 | 
			
		||||
 | 
			
		||||
        irq.disable();
 | 
			
		||||
        irq.set_handler(Self::on_interrupt);
 | 
			
		||||
        irq.pend();
 | 
			
		||||
        irq.enable();
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner {
 | 
			
		||||
                _peri: peri,
 | 
			
		||||
                timer,
 | 
			
		||||
                _ppi_ch1: ppi_ch1,
 | 
			
		||||
                _ppi_ch2: ppi_ch2,
 | 
			
		||||
 | 
			
		||||
                rx: RingBuffer::new(rx_buffer),
 | 
			
		||||
                rx_state: RxState::Idle,
 | 
			
		||||
                rx_waker: WakerRegistration::new(),
 | 
			
		||||
 | 
			
		||||
                tx: RingBuffer::new(tx_buffer),
 | 
			
		||||
                tx_state: TxState::Idle,
 | 
			
		||||
                tx_waker: WakerRegistration::new(),
 | 
			
		||||
            })),
 | 
			
		||||
            _peri: peri,
 | 
			
		||||
            timer,
 | 
			
		||||
            _ppi_ch1: ppi_ch1,
 | 
			
		||||
            _ppi_ch2: ppi_ch2,
 | 
			
		||||
            _ppi_group: ppi_group,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn pend_irq() {
 | 
			
		||||
        unsafe { <U::Interrupt as Interrupt>::steal() }.pend()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn on_interrupt(_: *mut ()) {
 | 
			
		||||
        //trace!("irq: start");
 | 
			
		||||
        let r = U::regs();
 | 
			
		||||
        let s = U::buffered_state();
 | 
			
		||||
 | 
			
		||||
        let buf_len = s.rx_buf.len();
 | 
			
		||||
        let half_len = buf_len / 2;
 | 
			
		||||
        let mut tx = unsafe { s.tx_buf.reader() };
 | 
			
		||||
        let mut rx = unsafe { s.rx_buf.writer() };
 | 
			
		||||
 | 
			
		||||
        if r.events_error.read().bits() != 0 {
 | 
			
		||||
            r.events_error.reset();
 | 
			
		||||
            let errs = r.errorsrc.read();
 | 
			
		||||
            r.errorsrc.write(|w| unsafe { w.bits(errs.bits()) });
 | 
			
		||||
 | 
			
		||||
            if errs.overrun().bit() {
 | 
			
		||||
                panic!("BufferedUarte overrun");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Received some bytes, wake task.
 | 
			
		||||
        if r.inten.read().rxdrdy().bit_is_set() && r.events_rxdrdy.read().bits() != 0 {
 | 
			
		||||
            r.intenclr.write(|w| w.rxdrdy().clear());
 | 
			
		||||
            r.events_rxdrdy.reset();
 | 
			
		||||
            s.rx_waker.wake();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If not RXing, start.
 | 
			
		||||
        if s.rx_bufs.load(Ordering::Relaxed) == 0 {
 | 
			
		||||
            let (ptr, len) = rx.push_buf();
 | 
			
		||||
            if len >= half_len {
 | 
			
		||||
                //trace!("  irq_rx: starting {:?}", half_len);
 | 
			
		||||
                s.rx_bufs.store(1, Ordering::Relaxed);
 | 
			
		||||
 | 
			
		||||
                // Set up the DMA read
 | 
			
		||||
                r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
 | 
			
		||||
                r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(half_len as _) });
 | 
			
		||||
 | 
			
		||||
                // Start UARTE Receive transaction
 | 
			
		||||
                r.tasks_startrx.write(|w| unsafe { w.bits(1) });
 | 
			
		||||
                rx.push_done(half_len);
 | 
			
		||||
                r.intenset.write(|w| w.rxstarted().set());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if r.events_rxstarted.read().bits() != 0 {
 | 
			
		||||
            //trace!("  irq_rx: rxstarted");
 | 
			
		||||
            let (ptr, len) = rx.push_buf();
 | 
			
		||||
            if len >= half_len {
 | 
			
		||||
                //trace!("  irq_rx: starting second {:?}", half_len);
 | 
			
		||||
 | 
			
		||||
                // Set up the DMA read
 | 
			
		||||
                r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
 | 
			
		||||
                r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(half_len as _) });
 | 
			
		||||
 | 
			
		||||
                let chn = s.rx_ppi_ch.load(Ordering::Relaxed);
 | 
			
		||||
 | 
			
		||||
                ppi::regs().chenset.write(|w| unsafe { w.bits(1 << chn) });
 | 
			
		||||
 | 
			
		||||
                rx.push_done(half_len);
 | 
			
		||||
 | 
			
		||||
                r.events_rxstarted.reset();
 | 
			
		||||
            } else {
 | 
			
		||||
                //trace!("  irq_rx: rxstarted no buf");
 | 
			
		||||
                r.intenclr.write(|w| w.rxstarted().clear());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // =============================
 | 
			
		||||
 | 
			
		||||
        // TX end
 | 
			
		||||
        if r.events_endtx.read().bits() != 0 {
 | 
			
		||||
            r.events_endtx.reset();
 | 
			
		||||
 | 
			
		||||
            let n = s.tx_count.load(Ordering::Relaxed);
 | 
			
		||||
            //trace!("  irq_tx: endtx {:?}", n);
 | 
			
		||||
            tx.pop_done(n);
 | 
			
		||||
            s.tx_waker.wake();
 | 
			
		||||
            s.tx_count.store(0, Ordering::Relaxed);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If not TXing, start.
 | 
			
		||||
        if s.tx_count.load(Ordering::Relaxed) == 0 {
 | 
			
		||||
            let (ptr, len) = tx.pop_buf();
 | 
			
		||||
            if len != 0 {
 | 
			
		||||
                //trace!("  irq_tx: starting {:?}", len);
 | 
			
		||||
                s.tx_count.store(len, Ordering::Relaxed);
 | 
			
		||||
 | 
			
		||||
                // Set up the DMA write
 | 
			
		||||
                r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
 | 
			
		||||
                r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
 | 
			
		||||
 | 
			
		||||
                // Start UARTE Transmit transaction
 | 
			
		||||
                r.tasks_starttx.write(|w| unsafe { w.bits(1) });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //trace!("irq: end");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Adjust the baud rate to the provided value.
 | 
			
		||||
    pub fn set_baudrate(&mut self, baudrate: Baudrate) {
 | 
			
		||||
        self.inner.borrow_mut().with(|state| {
 | 
			
		||||
            let r = U::regs();
 | 
			
		||||
 | 
			
		||||
            let timeout = 0x8000_0000 / (baudrate as u32 / 40);
 | 
			
		||||
            state.timer.cc(0).write(timeout);
 | 
			
		||||
            state.timer.clear();
 | 
			
		||||
 | 
			
		||||
            r.baudrate.write(|w| w.baudrate().variant(baudrate));
 | 
			
		||||
        });
 | 
			
		||||
        let r = U::regs();
 | 
			
		||||
        r.baudrate.write(|w| w.baudrate().variant(baudrate));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Split the UART in reader and writer parts.
 | 
			
		||||
@@ -206,120 +388,142 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
 | 
			
		||||
        (BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result<usize, core::convert::Infallible> {
 | 
			
		||||
    async fn inner_read(&self, buf: &mut [u8]) -> Result<usize, Error> {
 | 
			
		||||
        let data = self.inner_fill_buf().await?;
 | 
			
		||||
        let n = data.len().min(buf.len());
 | 
			
		||||
        buf[..n].copy_from_slice(&data[..n]);
 | 
			
		||||
        self.inner_consume(n);
 | 
			
		||||
        Ok(n)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result<usize, Error> {
 | 
			
		||||
        poll_fn(move |cx| {
 | 
			
		||||
            let mut do_pend = false;
 | 
			
		||||
            let mut inner = self.inner.borrow_mut();
 | 
			
		||||
            let res = inner.with(|state| {
 | 
			
		||||
                compiler_fence(Ordering::SeqCst);
 | 
			
		||||
                trace!("poll_read");
 | 
			
		||||
            //trace!("poll_write: {:?}", buf.len());
 | 
			
		||||
            let s = U::buffered_state();
 | 
			
		||||
            let mut tx = unsafe { s.tx_buf.writer() };
 | 
			
		||||
 | 
			
		||||
                // We have data ready in buffer? Return it.
 | 
			
		||||
                let data = state.rx.pop_buf();
 | 
			
		||||
                if !data.is_empty() {
 | 
			
		||||
                    trace!("  got {:?} {:?}", data.as_ptr() as u32, data.len());
 | 
			
		||||
                    let len = data.len().min(buf.len());
 | 
			
		||||
                    buf[..len].copy_from_slice(&data[..len]);
 | 
			
		||||
                    state.rx.pop(len);
 | 
			
		||||
                    do_pend = true;
 | 
			
		||||
                    return Poll::Ready(Ok(len));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                trace!("  empty");
 | 
			
		||||
                state.rx_waker.register(cx.waker());
 | 
			
		||||
                Poll::Pending
 | 
			
		||||
            });
 | 
			
		||||
            if do_pend {
 | 
			
		||||
                inner.pend();
 | 
			
		||||
            let tx_buf = tx.push_slice();
 | 
			
		||||
            if tx_buf.is_empty() {
 | 
			
		||||
                //trace!("poll_write: pending");
 | 
			
		||||
                s.tx_waker.register(cx.waker());
 | 
			
		||||
                return Poll::Pending;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            res
 | 
			
		||||
            let n = min(tx_buf.len(), buf.len());
 | 
			
		||||
            tx_buf[..n].copy_from_slice(&buf[..n]);
 | 
			
		||||
            tx.push_done(n);
 | 
			
		||||
 | 
			
		||||
            //trace!("poll_write: queued {:?}", n);
 | 
			
		||||
 | 
			
		||||
            compiler_fence(Ordering::SeqCst);
 | 
			
		||||
            Self::pend_irq();
 | 
			
		||||
 | 
			
		||||
            Poll::Ready(Ok(n))
 | 
			
		||||
        })
 | 
			
		||||
        .await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result<usize, core::convert::Infallible> {
 | 
			
		||||
    async fn inner_flush<'a>(&'a self) -> Result<(), Error> {
 | 
			
		||||
        poll_fn(move |cx| {
 | 
			
		||||
            let mut inner = self.inner.borrow_mut();
 | 
			
		||||
            let res = inner.with(|state| {
 | 
			
		||||
                trace!("poll_write: {:?}", buf.len());
 | 
			
		||||
            //trace!("poll_flush");
 | 
			
		||||
            let s = U::buffered_state();
 | 
			
		||||
            if !s.tx_buf.is_empty() {
 | 
			
		||||
                //trace!("poll_flush: pending");
 | 
			
		||||
                s.tx_waker.register(cx.waker());
 | 
			
		||||
                return Poll::Pending;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                let tx_buf = state.tx.push_buf();
 | 
			
		||||
                if tx_buf.is_empty() {
 | 
			
		||||
                    trace!("poll_write: pending");
 | 
			
		||||
                    state.tx_waker.register(cx.waker());
 | 
			
		||||
                    return Poll::Pending;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let n = min(tx_buf.len(), buf.len());
 | 
			
		||||
                tx_buf[..n].copy_from_slice(&buf[..n]);
 | 
			
		||||
                state.tx.push(n);
 | 
			
		||||
 | 
			
		||||
                trace!("poll_write: queued {:?}", n);
 | 
			
		||||
 | 
			
		||||
                compiler_fence(Ordering::SeqCst);
 | 
			
		||||
 | 
			
		||||
                Poll::Ready(Ok(n))
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            inner.pend();
 | 
			
		||||
 | 
			
		||||
            res
 | 
			
		||||
            Poll::Ready(Ok(()))
 | 
			
		||||
        })
 | 
			
		||||
        .await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn inner_flush<'a>(&'a self) -> Result<(), core::convert::Infallible> {
 | 
			
		||||
    async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> {
 | 
			
		||||
        poll_fn(move |cx| {
 | 
			
		||||
            self.inner.borrow_mut().with(|state| {
 | 
			
		||||
                trace!("poll_flush");
 | 
			
		||||
            compiler_fence(Ordering::SeqCst);
 | 
			
		||||
            //trace!("poll_read");
 | 
			
		||||
 | 
			
		||||
                if !state.tx.is_empty() {
 | 
			
		||||
                    trace!("poll_flush: pending");
 | 
			
		||||
                    state.tx_waker.register(cx.waker());
 | 
			
		||||
                    return Poll::Pending;
 | 
			
		||||
                }
 | 
			
		||||
            let r = U::regs();
 | 
			
		||||
            let s = U::buffered_state();
 | 
			
		||||
 | 
			
		||||
                Poll::Ready(Ok(()))
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
        .await
 | 
			
		||||
    }
 | 
			
		||||
            // Read the RXDRDY counter.
 | 
			
		||||
            T::regs().tasks_capture[0].write(|w| unsafe { w.bits(1) });
 | 
			
		||||
            let mut end = T::regs().cc[0].read().bits() as usize;
 | 
			
		||||
            //trace!("  rxdrdy count = {:?}", end);
 | 
			
		||||
 | 
			
		||||
    async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], core::convert::Infallible> {
 | 
			
		||||
        poll_fn(move |cx| {
 | 
			
		||||
            self.inner.borrow_mut().with(|state| {
 | 
			
		||||
                compiler_fence(Ordering::SeqCst);
 | 
			
		||||
                trace!("fill_buf");
 | 
			
		||||
            // We've set a compare channel that resets the counter to 0 when it reaches `len*2`.
 | 
			
		||||
            // However, it's unclear if that's instant, or there's a small window where you can
 | 
			
		||||
            // still read `len()*2`.
 | 
			
		||||
            // This could happen if in one clock cycle the counter is updated, and in the next the
 | 
			
		||||
            // clear takes effect. The docs are very sparse, they just say "Task delays: After TIMER
 | 
			
		||||
            // is started, the CLEAR, COUNT, and STOP tasks are guaranteed to take effect within one
 | 
			
		||||
            // clock cycle of the PCLK16M." :shrug:
 | 
			
		||||
            // So, we wrap the counter ourselves, just in case.
 | 
			
		||||
            if end > s.rx_buf.len() * 2 {
 | 
			
		||||
                end = 0
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                // We have data ready in buffer? Return it.
 | 
			
		||||
                let buf = state.rx.pop_buf();
 | 
			
		||||
                if !buf.is_empty() {
 | 
			
		||||
                    trace!("  got {:?} {:?}", buf.as_ptr() as u32, buf.len());
 | 
			
		||||
                    let buf: &[u8] = buf;
 | 
			
		||||
                    // Safety: buffer lives as long as uart
 | 
			
		||||
                    let buf: &[u8] = unsafe { core::mem::transmute(buf) };
 | 
			
		||||
                    return Poll::Ready(Ok(buf));
 | 
			
		||||
                }
 | 
			
		||||
            // This logic mirrors `atomic_ring_buffer::Reader::pop_buf()`
 | 
			
		||||
            let mut start = s.rx_buf.start.load(Ordering::Relaxed);
 | 
			
		||||
            let len = s.rx_buf.len();
 | 
			
		||||
            if start == end {
 | 
			
		||||
                //trace!("  empty");
 | 
			
		||||
                s.rx_waker.register(cx.waker());
 | 
			
		||||
                r.intenset.write(|w| w.rxdrdy().set_bit());
 | 
			
		||||
                return Poll::Pending;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                trace!("  empty");
 | 
			
		||||
                state.rx_waker.register(cx.waker());
 | 
			
		||||
                Poll::<Result<&[u8], core::convert::Infallible>>::Pending
 | 
			
		||||
            })
 | 
			
		||||
            if start >= len {
 | 
			
		||||
                start -= len
 | 
			
		||||
            }
 | 
			
		||||
            if end >= len {
 | 
			
		||||
                end -= len
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let n = if end > start { end - start } else { len - start };
 | 
			
		||||
            assert!(n != 0);
 | 
			
		||||
            //trace!("  uarte ringbuf: pop_buf {:?}..{:?}", start, start + n);
 | 
			
		||||
 | 
			
		||||
            let buf = s.rx_buf.buf.load(Ordering::Relaxed);
 | 
			
		||||
            Poll::Ready(Ok(unsafe { slice::from_raw_parts(buf.add(start), n) }))
 | 
			
		||||
        })
 | 
			
		||||
        .await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn inner_consume(&self, amt: usize) {
 | 
			
		||||
        let mut inner = self.inner.borrow_mut();
 | 
			
		||||
        let signal = inner.with(|state| {
 | 
			
		||||
            let full = state.rx.is_full();
 | 
			
		||||
            state.rx.pop(amt);
 | 
			
		||||
            full
 | 
			
		||||
        });
 | 
			
		||||
        if signal {
 | 
			
		||||
            inner.pend();
 | 
			
		||||
        if amt == 0 {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let s = U::buffered_state();
 | 
			
		||||
        let mut rx = unsafe { s.rx_buf.reader() };
 | 
			
		||||
        rx.pop_done(amt);
 | 
			
		||||
        U::regs().intenset.write(|w| w.rxstarted().set());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
 | 
			
		||||
    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
 | 
			
		||||
        self.inner_read(buf).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.
 | 
			
		||||
    pub async fn fill_buf(&mut self) -> Result<&[u8], Error> {
 | 
			
		||||
        self.inner_fill_buf().await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.
 | 
			
		||||
    pub fn consume(&mut self, amt: usize) {
 | 
			
		||||
        self.inner_consume(amt)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Write a buffer into this writer, returning how many bytes were written.
 | 
			
		||||
    pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
 | 
			
		||||
        self.inner_write(buf).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
 | 
			
		||||
    pub async fn flush(&mut self) -> Result<(), Error> {
 | 
			
		||||
        self.inner_flush().await
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -328,76 +532,116 @@ pub struct BufferedUarteTx<'u, 'd, U: UarteInstance, T: TimerInstance> {
 | 
			
		||||
    inner: &'u BufferedUarte<'d, U, T>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteTx<'u, 'd, U, T> {
 | 
			
		||||
    /// Write a buffer into this writer, returning how many bytes were written.
 | 
			
		||||
    pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
 | 
			
		||||
        self.inner.inner_write(buf).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
 | 
			
		||||
    pub async fn flush(&mut self) -> Result<(), Error> {
 | 
			
		||||
        self.inner.inner_flush().await
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Writer part of the buffered UARTE driver.
 | 
			
		||||
pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> {
 | 
			
		||||
    inner: &'u BufferedUarte<'d, U, T>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> {
 | 
			
		||||
    type Error = core::convert::Infallible;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> {
 | 
			
		||||
    type Error = core::convert::Infallible;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> {
 | 
			
		||||
    type Error = core::convert::Infallible;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> {
 | 
			
		||||
    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
 | 
			
		||||
        self.inner_read(buf).await
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> {
 | 
			
		||||
    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
 | 
			
		||||
impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'u, 'd, U, T> {
 | 
			
		||||
    /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
 | 
			
		||||
    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
 | 
			
		||||
        self.inner.inner_read(buf).await
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> {
 | 
			
		||||
    async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
 | 
			
		||||
        self.inner_fill_buf().await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn consume(&mut self, amt: usize) {
 | 
			
		||||
        self.inner_consume(amt)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> {
 | 
			
		||||
    async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
 | 
			
		||||
    /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.
 | 
			
		||||
    pub async fn fill_buf(&mut self) -> Result<&[u8], Error> {
 | 
			
		||||
        self.inner.inner_fill_buf().await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn consume(&mut self, amt: usize) {
 | 
			
		||||
    /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.
 | 
			
		||||
    pub fn consume(&mut self, amt: usize) {
 | 
			
		||||
        self.inner.inner_consume(amt)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> {
 | 
			
		||||
    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
 | 
			
		||||
        self.inner_write(buf).await
 | 
			
		||||
#[cfg(feature = "nightly")]
 | 
			
		||||
mod _embedded_io {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    impl embedded_io::Error for Error {
 | 
			
		||||
        fn kind(&self) -> embedded_io::ErrorKind {
 | 
			
		||||
            match *self {}
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn flush(&mut self) -> Result<(), Self::Error> {
 | 
			
		||||
        self.inner_flush().await
 | 
			
		||||
    impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> {
 | 
			
		||||
        type Error = Error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> {
 | 
			
		||||
        type Error = Error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> {
 | 
			
		||||
        type Error = Error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> {
 | 
			
		||||
        async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
 | 
			
		||||
            self.inner_read(buf).await
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> {
 | 
			
		||||
        async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
 | 
			
		||||
            self.inner.inner_read(buf).await
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> {
 | 
			
		||||
        async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
 | 
			
		||||
            self.inner_fill_buf().await
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn consume(&mut self, amt: usize) {
 | 
			
		||||
            self.inner_consume(amt)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> {
 | 
			
		||||
        async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
 | 
			
		||||
            self.inner.inner_fill_buf().await
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn consume(&mut self, amt: usize) {
 | 
			
		||||
            self.inner.inner_consume(amt)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> {
 | 
			
		||||
        async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
 | 
			
		||||
            self.inner_write(buf).await
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        async fn flush(&mut self) -> Result<(), Self::Error> {
 | 
			
		||||
            self.inner_flush().await
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> {
 | 
			
		||||
        async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
 | 
			
		||||
            self.inner.inner_write(buf).await
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        async fn flush(&mut self) -> Result<(), Self::Error> {
 | 
			
		||||
            self.inner.inner_flush().await
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> {
 | 
			
		||||
    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
 | 
			
		||||
        self.inner.inner_write(buf).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn flush(&mut self) -> Result<(), Self::Error> {
 | 
			
		||||
        self.inner.inner_flush().await
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> {
 | 
			
		||||
impl<'a, U: UarteInstance, T: TimerInstance> Drop for BufferedUarte<'a, U, T> {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        let r = U::regs();
 | 
			
		||||
 | 
			
		||||
@@ -418,108 +662,11 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> {
 | 
			
		||||
        gpio::deconfigure_pin(r.psel.txd.read().bits());
 | 
			
		||||
        gpio::deconfigure_pin(r.psel.rts.read().bits());
 | 
			
		||||
        gpio::deconfigure_pin(r.psel.cts.read().bits());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a, U: UarteInstance, T: TimerInstance> PeripheralState for StateInner<'a, U, T> {
 | 
			
		||||
    type Interrupt = U::Interrupt;
 | 
			
		||||
    fn on_interrupt(&mut self) {
 | 
			
		||||
        trace!("irq: start");
 | 
			
		||||
        let r = U::regs();
 | 
			
		||||
 | 
			
		||||
        loop {
 | 
			
		||||
            match self.rx_state {
 | 
			
		||||
                RxState::Idle => {
 | 
			
		||||
                    trace!("  irq_rx: in state idle");
 | 
			
		||||
 | 
			
		||||
                    let buf = self.rx.push_buf();
 | 
			
		||||
                    if !buf.is_empty() {
 | 
			
		||||
                        trace!("  irq_rx: starting {:?}", buf.len());
 | 
			
		||||
                        self.rx_state = RxState::Receiving;
 | 
			
		||||
 | 
			
		||||
                        // Set up the DMA read
 | 
			
		||||
                        r.rxd.ptr.write(|w|
 | 
			
		||||
                            // The PTR field is a full 32 bits wide and accepts the full range
 | 
			
		||||
                            // of values.
 | 
			
		||||
                            unsafe { w.ptr().bits(buf.as_ptr() as u32) });
 | 
			
		||||
                        r.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(buf.len() as _) });
 | 
			
		||||
                        trace!("  irq_rx: buf {:?} {:?}", buf.as_ptr() as u32, buf.len());
 | 
			
		||||
 | 
			
		||||
                        // Start UARTE Receive transaction
 | 
			
		||||
                        r.tasks_startrx.write(|w| unsafe { w.bits(1) });
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                RxState::Receiving => {
 | 
			
		||||
                    trace!("  irq_rx: in state receiving");
 | 
			
		||||
                    if r.events_endrx.read().bits() != 0 {
 | 
			
		||||
                        self.timer.stop();
 | 
			
		||||
 | 
			
		||||
                        let n: usize = r.rxd.amount.read().amount().bits() as usize;
 | 
			
		||||
                        trace!("  irq_rx: endrx {:?}", n);
 | 
			
		||||
                        self.rx.push(n);
 | 
			
		||||
 | 
			
		||||
                        r.events_endrx.reset();
 | 
			
		||||
 | 
			
		||||
                        self.rx_waker.wake();
 | 
			
		||||
                        self.rx_state = RxState::Idle;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        loop {
 | 
			
		||||
            match self.tx_state {
 | 
			
		||||
                TxState::Idle => {
 | 
			
		||||
                    trace!("  irq_tx: in state Idle");
 | 
			
		||||
                    let buf = self.tx.pop_buf();
 | 
			
		||||
                    if !buf.is_empty() {
 | 
			
		||||
                        trace!("  irq_tx: starting {:?}", buf.len());
 | 
			
		||||
                        self.tx_state = TxState::Transmitting(buf.len());
 | 
			
		||||
 | 
			
		||||
                        // Set up the DMA write
 | 
			
		||||
                        r.txd.ptr.write(|w|
 | 
			
		||||
                            // The PTR field is a full 32 bits wide and accepts the full range
 | 
			
		||||
                            // of values.
 | 
			
		||||
                            unsafe { w.ptr().bits(buf.as_ptr() as u32) });
 | 
			
		||||
                        r.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(buf.len() as _) });
 | 
			
		||||
 | 
			
		||||
                        // Start UARTE Transmit transaction
 | 
			
		||||
                        r.tasks_starttx.write(|w| unsafe { w.bits(1) });
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                TxState::Transmitting(n) => {
 | 
			
		||||
                    trace!("  irq_tx: in state Transmitting");
 | 
			
		||||
                    if r.events_endtx.read().bits() != 0 {
 | 
			
		||||
                        r.events_endtx.reset();
 | 
			
		||||
 | 
			
		||||
                        trace!("  irq_tx: endtx {:?}", n);
 | 
			
		||||
                        self.tx.pop(n);
 | 
			
		||||
                        self.tx_waker.wake();
 | 
			
		||||
                        self.tx_state = TxState::Idle;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        trace!("irq: end");
 | 
			
		||||
 | 
			
		||||
        let s = U::buffered_state();
 | 
			
		||||
        unsafe {
 | 
			
		||||
            s.rx_buf.deinit();
 | 
			
		||||
            s.tx_buf.deinit();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,6 @@ pub(crate) mod util;
 | 
			
		||||
#[cfg(feature = "_time-driver")]
 | 
			
		||||
mod time_driver;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "nightly")]
 | 
			
		||||
pub mod buffered_uarte;
 | 
			
		||||
pub mod gpio;
 | 
			
		||||
#[cfg(feature = "gpiote")]
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ use crate::{pac, Peripheral};
 | 
			
		||||
const DPPI_ENABLE_BIT: u32 = 0x8000_0000;
 | 
			
		||||
const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF;
 | 
			
		||||
 | 
			
		||||
fn regs() -> &'static pac::dppic::RegisterBlock {
 | 
			
		||||
pub(crate) fn regs() -> &'static pac::dppic::RegisterBlock {
 | 
			
		||||
    unsafe { &*pac::DPPIC::ptr() }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,16 +17,16 @@
 | 
			
		||||
 | 
			
		||||
use core::ptr::NonNull;
 | 
			
		||||
 | 
			
		||||
use embassy_hal_common::{impl_peripheral, PeripheralRef};
 | 
			
		||||
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
 | 
			
		||||
 | 
			
		||||
use crate::{peripherals, Peripheral};
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "_dppi")]
 | 
			
		||||
mod dppi;
 | 
			
		||||
#[cfg(feature = "_ppi")]
 | 
			
		||||
mod ppi;
 | 
			
		||||
#[cfg_attr(feature = "_dppi", path = "dppi.rs")]
 | 
			
		||||
#[cfg_attr(feature = "_ppi", path = "ppi.rs")]
 | 
			
		||||
mod _version;
 | 
			
		||||
pub(crate) use _version::*;
 | 
			
		||||
 | 
			
		||||
/// An instance of the Programmable peripheral interconnect on nRF devices.
 | 
			
		||||
/// PPI channel driver.
 | 
			
		||||
pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
 | 
			
		||||
    ch: PeripheralRef<'d, C>,
 | 
			
		||||
    #[cfg(feature = "_dppi")]
 | 
			
		||||
@@ -35,6 +35,88 @@ pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize
 | 
			
		||||
    tasks: [Task; TASK_COUNT],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// PPI channel group driver.
 | 
			
		||||
pub struct PpiGroup<'d, G: Group> {
 | 
			
		||||
    g: PeripheralRef<'d, G>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, G: Group> PpiGroup<'d, G> {
 | 
			
		||||
    /// Create a new PPI group driver.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The group is initialized as containing no channels.
 | 
			
		||||
    pub fn new(g: impl Peripheral<P = G> + 'd) -> Self {
 | 
			
		||||
        into_ref!(g);
 | 
			
		||||
 | 
			
		||||
        let r = regs();
 | 
			
		||||
        let n = g.number();
 | 
			
		||||
        r.chg[n].write(|w| unsafe { w.bits(0) });
 | 
			
		||||
 | 
			
		||||
        Self { g }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Add a PPI channel to this group.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If the channel is already in the group, this is a no-op.
 | 
			
		||||
    pub fn add_channel<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let r = regs();
 | 
			
		||||
        let ng = self.g.number();
 | 
			
		||||
        let nc = ch.ch.number();
 | 
			
		||||
        r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() | 1 << nc) });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Remove a PPI channel from this group.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If the channel is already not in the group, this is a no-op.
 | 
			
		||||
    pub fn remove_channel<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let r = regs();
 | 
			
		||||
        let ng = self.g.number();
 | 
			
		||||
        let nc = ch.ch.number();
 | 
			
		||||
        r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() & !(1 << nc)) });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Enable all the channels in this group.
 | 
			
		||||
    pub fn enable_all(&mut self) {
 | 
			
		||||
        let n = self.g.number();
 | 
			
		||||
        regs().tasks_chg[n].en.write(|w| unsafe { w.bits(1) });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Disable all the channels in this group.
 | 
			
		||||
    pub fn disable_all(&mut self) {
 | 
			
		||||
        let n = self.g.number();
 | 
			
		||||
        regs().tasks_chg[n].dis.write(|w| unsafe { w.bits(1) });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get a reference to the "enable all" task.
 | 
			
		||||
    ///
 | 
			
		||||
    /// When triggered, it will enable all the channels in this group.
 | 
			
		||||
    pub fn task_enable_all(&self) -> Task {
 | 
			
		||||
        let n = self.g.number();
 | 
			
		||||
        Task::from_reg(®s().tasks_chg[n].en)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get a reference to the "disable all" task.
 | 
			
		||||
    ///
 | 
			
		||||
    /// When triggered, it will disable all the channels in this group.
 | 
			
		||||
    pub fn task_disable_all(&self) -> Task {
 | 
			
		||||
        let n = self.g.number();
 | 
			
		||||
        Task::from_reg(®s().tasks_chg[n].dis)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, G: Group> Drop for PpiGroup<'d, G> {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        let r = regs();
 | 
			
		||||
        let n = self.g.number();
 | 
			
		||||
        r.chg[n].write(|w| unsafe { w.bits(0) });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "_dppi")]
 | 
			
		||||
const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
 | 
			
		||||
 | 
			
		||||
@@ -112,7 +194,7 @@ pub(crate) mod sealed {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Interface for PPI channels.
 | 
			
		||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + Sized {
 | 
			
		||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + Sized + 'static {
 | 
			
		||||
    /// Returns the number of the channel
 | 
			
		||||
    fn number(&self) -> usize;
 | 
			
		||||
}
 | 
			
		||||
@@ -130,7 +212,7 @@ pub trait StaticChannel: Channel + Into<AnyStaticChannel> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Interface for a group of PPI channels.
 | 
			
		||||
pub trait Group: sealed::Group + Sized {
 | 
			
		||||
pub trait Group: sealed::Group + Peripheral<P = Self> + Into<AnyGroup> + Sized + 'static {
 | 
			
		||||
    /// Returns the number of the group.
 | 
			
		||||
    fn number(&self) -> usize;
 | 
			
		||||
    /// Convert into a type erased group.
 | 
			
		||||
@@ -248,6 +330,12 @@ macro_rules! impl_group {
 | 
			
		||||
                $number
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl From<peripherals::$type> for crate::ppi::AnyGroup {
 | 
			
		||||
            fn from(val: peripherals::$type) -> Self {
 | 
			
		||||
                crate::ppi::Group::degrade(val)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ impl Event {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn regs() -> &'static pac::ppi::RegisterBlock {
 | 
			
		||||
pub(crate) fn regs() -> &'static pac::ppi::RegisterBlock {
 | 
			
		||||
    unsafe { &*pac::PPI::ptr() }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -132,7 +132,21 @@ impl<'d, T: Instance> Timer<'d, T, Awaitable> {
 | 
			
		||||
        irq.unpend();
 | 
			
		||||
        irq.enable();
 | 
			
		||||
 | 
			
		||||
        Self::new_inner(timer)
 | 
			
		||||
        Self::new_inner(timer, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a new async-capable timer driver in counter mode.
 | 
			
		||||
    pub fn new_awaitable_counter(
 | 
			
		||||
        timer: impl Peripheral<P = T> + 'd,
 | 
			
		||||
        irq: impl Peripheral<P = T::Interrupt> + 'd,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        into_ref!(irq);
 | 
			
		||||
 | 
			
		||||
        irq.set_handler(Self::on_interrupt);
 | 
			
		||||
        irq.unpend();
 | 
			
		||||
        irq.enable();
 | 
			
		||||
 | 
			
		||||
        Self::new_inner(timer, true)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -142,7 +156,15 @@ impl<'d, T: Instance> Timer<'d, T, NotAwaitable> {
 | 
			
		||||
    /// This can be useful for triggering tasks via PPI
 | 
			
		||||
    /// `Uarte` uses this internally.
 | 
			
		||||
    pub fn new(timer: impl Peripheral<P = T> + 'd) -> Self {
 | 
			
		||||
        Self::new_inner(timer)
 | 
			
		||||
        Self::new_inner(timer, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a `Timer` driver in counter mode without an interrupt, meaning `Cc::wait` won't work.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This can be useful for triggering tasks via PPI
 | 
			
		||||
    /// `Uarte` uses this internally.
 | 
			
		||||
    pub fn new_counter(timer: impl Peripheral<P = T> + 'd) -> Self {
 | 
			
		||||
        Self::new_inner(timer, true)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -150,7 +172,7 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> {
 | 
			
		||||
    /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is used by the public constructors.
 | 
			
		||||
    fn new_inner(timer: impl Peripheral<P = T> + 'd) -> Self {
 | 
			
		||||
    fn new_inner(timer: impl Peripheral<P = T> + 'd, is_counter: bool) -> Self {
 | 
			
		||||
        into_ref!(timer);
 | 
			
		||||
 | 
			
		||||
        let regs = T::regs();
 | 
			
		||||
@@ -164,8 +186,11 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> {
 | 
			
		||||
        // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification.
 | 
			
		||||
        this.stop();
 | 
			
		||||
 | 
			
		||||
        // Set the instance to timer mode.
 | 
			
		||||
        regs.mode.write(|w| w.mode().timer());
 | 
			
		||||
        if is_counter {
 | 
			
		||||
            regs.mode.write(|w| w.mode().counter());
 | 
			
		||||
        } else {
 | 
			
		||||
            regs.mode.write(|w| w.mode().timer());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Make the counter's max value as high as possible.
 | 
			
		||||
        // TODO: is there a reason someone would want to set this lower?
 | 
			
		||||
@@ -225,6 +250,14 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> {
 | 
			
		||||
        Task::from_reg(&T::regs().tasks_clear)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the COUNT task, for use with PPI.
 | 
			
		||||
    ///
 | 
			
		||||
    /// When triggered, this task increments the timer's counter by 1.
 | 
			
		||||
    /// Only works in counter mode.
 | 
			
		||||
    pub fn task_count(&self) -> Task {
 | 
			
		||||
        Task::from_reg(&T::regs().tasks_count)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Change the timer's frequency.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This will stop the timer if it isn't already stopped,
 | 
			
		||||
 
 | 
			
		||||
@@ -883,6 +883,7 @@ pub(crate) mod sealed {
 | 
			
		||||
    pub trait Instance {
 | 
			
		||||
        fn regs() -> &'static pac::uarte0::RegisterBlock;
 | 
			
		||||
        fn state() -> &'static State;
 | 
			
		||||
        fn buffered_state() -> &'static crate::buffered_uarte::State;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -902,6 +903,10 @@ macro_rules! impl_uarte {
 | 
			
		||||
                static STATE: crate::uarte::sealed::State = crate::uarte::sealed::State::new();
 | 
			
		||||
                &STATE
 | 
			
		||||
            }
 | 
			
		||||
            fn buffered_state() -> &'static crate::buffered_uarte::State {
 | 
			
		||||
                static STATE: crate::buffered_uarte::State = crate::buffered_uarte::State::new();
 | 
			
		||||
                &STATE
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        impl crate::uarte::Instance for peripherals::$type {
 | 
			
		||||
            type Interrupt = crate::interrupt::$irq;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,9 @@
 | 
			
		||||
 | 
			
		||||
use defmt::*;
 | 
			
		||||
use embassy_executor::Spawner;
 | 
			
		||||
use embassy_nrf::buffered_uarte::{BufferedUarte, State};
 | 
			
		||||
use embassy_nrf::buffered_uarte::BufferedUarte;
 | 
			
		||||
use embassy_nrf::{interrupt, uarte};
 | 
			
		||||
use embedded_io::asynch::{BufRead, Write};
 | 
			
		||||
use futures::pin_mut;
 | 
			
		||||
use embedded_io::asynch::Write;
 | 
			
		||||
use {defmt_rtt as _, panic_probe as _};
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::main]
 | 
			
		||||
@@ -21,24 +20,19 @@ async fn main(_spawner: Spawner) {
 | 
			
		||||
    let mut rx_buffer = [0u8; 4096];
 | 
			
		||||
 | 
			
		||||
    let irq = interrupt::take!(UARTE0_UART0);
 | 
			
		||||
    let mut state = State::new();
 | 
			
		||||
    // Please note - important to have hardware flow control (https://github.com/embassy-rs/embassy/issues/536)
 | 
			
		||||
    let u = BufferedUarte::new(
 | 
			
		||||
        &mut state,
 | 
			
		||||
    let mut u = BufferedUarte::new(
 | 
			
		||||
        p.UARTE0,
 | 
			
		||||
        p.TIMER0,
 | 
			
		||||
        p.PPI_CH0,
 | 
			
		||||
        p.PPI_CH1,
 | 
			
		||||
        p.PPI_GROUP0,
 | 
			
		||||
        irq,
 | 
			
		||||
        p.P0_08,
 | 
			
		||||
        p.P0_06,
 | 
			
		||||
        p.P0_07,
 | 
			
		||||
        p.P0_05,
 | 
			
		||||
        config,
 | 
			
		||||
        &mut rx_buffer,
 | 
			
		||||
        &mut tx_buffer,
 | 
			
		||||
    );
 | 
			
		||||
    pin_mut!(u);
 | 
			
		||||
 | 
			
		||||
    info!("uarte initialized!");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								tests/nrf/.cargo/config.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/nrf/.cargo/config.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
 | 
			
		||||
#runner = "teleprobe local run --chip nRF52840_xxAA --elf"
 | 
			
		||||
runner = "teleprobe client run --target nrf52840-dk --elf"
 | 
			
		||||
 | 
			
		||||
[build]
 | 
			
		||||
target = "thumbv7em-none-eabi"
 | 
			
		||||
 | 
			
		||||
[env]
 | 
			
		||||
DEFMT_LOG = "trace"
 | 
			
		||||
							
								
								
									
										20
									
								
								tests/nrf/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								tests/nrf/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
[package]
 | 
			
		||||
edition = "2021"
 | 
			
		||||
name = "embassy-nrf-examples"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
license = "MIT OR Apache-2.0"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
 | 
			
		||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] }
 | 
			
		||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] }
 | 
			
		||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] }
 | 
			
		||||
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
 | 
			
		||||
embedded-io = { version = "0.4.0", features = ["async"] }
 | 
			
		||||
 | 
			
		||||
defmt = "0.3"
 | 
			
		||||
defmt-rtt = "0.4"
 | 
			
		||||
 | 
			
		||||
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
 | 
			
		||||
cortex-m-rt = "0.7.0"
 | 
			
		||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
 | 
			
		||||
							
								
								
									
										16
									
								
								tests/nrf/build.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tests/nrf/build.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
use std::error::Error;
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::{env, fs};
 | 
			
		||||
 | 
			
		||||
fn main() -> Result<(), Box<dyn Error>> {
 | 
			
		||||
    let out = PathBuf::from(env::var("OUT_DIR").unwrap());
 | 
			
		||||
    fs::write(out.join("link_ram.x"), include_bytes!("link_ram.x")).unwrap();
 | 
			
		||||
    println!("cargo:rustc-link-search={}", out.display());
 | 
			
		||||
    println!("cargo:rerun-if-changed=link_ram.x");
 | 
			
		||||
 | 
			
		||||
    println!("cargo:rustc-link-arg-bins=--nmagic");
 | 
			
		||||
    println!("cargo:rustc-link-arg-bins=-Tlink_ram.x");
 | 
			
		||||
    println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										254
									
								
								tests/nrf/link_ram.x
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								tests/nrf/link_ram.x
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,254 @@
 | 
			
		||||
/* ##### EMBASSY NOTE
 | 
			
		||||
    Originally from https://github.com/rust-embedded/cortex-m-rt/blob/master/link.x.in
 | 
			
		||||
    Adjusted to put everything in RAM
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* # Developer notes
 | 
			
		||||
 | 
			
		||||
- Symbols that start with a double underscore (__) are considered "private"
 | 
			
		||||
 | 
			
		||||
- Symbols that start with a single underscore (_) are considered "semi-public"; they can be
 | 
			
		||||
  overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" {
 | 
			
		||||
  static mut __sbss }`).
 | 
			
		||||
 | 
			
		||||
- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a
 | 
			
		||||
  symbol if not dropped if it appears in or near the front of the linker arguments and "it's not
 | 
			
		||||
  needed" by any of the preceding objects (linker arguments)
 | 
			
		||||
 | 
			
		||||
- `PROVIDE` is used to provide default values that can be overridden by a user linker script
 | 
			
		||||
 | 
			
		||||
- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*
 | 
			
		||||
  the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization
 | 
			
		||||
  routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see
 | 
			
		||||
  "Address (..) is out of bounds" in the disassembly produced by `objdump`.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* Provides information about the memory layout of the device */
 | 
			
		||||
/* This will be provided by the user (see `memory.x`) or by a Board Support Crate */
 | 
			
		||||
INCLUDE memory.x
 | 
			
		||||
 | 
			
		||||
/* # Entry point = reset vector */
 | 
			
		||||
EXTERN(__RESET_VECTOR);
 | 
			
		||||
EXTERN(Reset);
 | 
			
		||||
ENTRY(Reset);
 | 
			
		||||
 | 
			
		||||
/* # Exception vectors */
 | 
			
		||||
/* This is effectively weak aliasing at the linker level */
 | 
			
		||||
/* The user can override any of these aliases by defining the corresponding symbol themselves (cf.
 | 
			
		||||
   the `exception!` macro) */
 | 
			
		||||
EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */
 | 
			
		||||
 | 
			
		||||
EXTERN(DefaultHandler);
 | 
			
		||||
 | 
			
		||||
PROVIDE(NonMaskableInt = DefaultHandler);
 | 
			
		||||
EXTERN(HardFaultTrampoline);
 | 
			
		||||
PROVIDE(MemoryManagement = DefaultHandler);
 | 
			
		||||
PROVIDE(BusFault = DefaultHandler);
 | 
			
		||||
PROVIDE(UsageFault = DefaultHandler);
 | 
			
		||||
PROVIDE(SecureFault = DefaultHandler);
 | 
			
		||||
PROVIDE(SVCall = DefaultHandler);
 | 
			
		||||
PROVIDE(DebugMonitor = DefaultHandler);
 | 
			
		||||
PROVIDE(PendSV = DefaultHandler);
 | 
			
		||||
PROVIDE(SysTick = DefaultHandler);
 | 
			
		||||
 | 
			
		||||
PROVIDE(DefaultHandler = DefaultHandler_);
 | 
			
		||||
PROVIDE(HardFault = HardFault_);
 | 
			
		||||
 | 
			
		||||
/* # Interrupt vectors */
 | 
			
		||||
EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */
 | 
			
		||||
 | 
			
		||||
/* # Pre-initialization function */
 | 
			
		||||
/* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function,
 | 
			
		||||
   then the function this points to will be called before the RAM is initialized. */
 | 
			
		||||
PROVIDE(__pre_init = DefaultPreInit);
 | 
			
		||||
 | 
			
		||||
/* # Sections */
 | 
			
		||||
SECTIONS
 | 
			
		||||
{
 | 
			
		||||
  PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM));
 | 
			
		||||
 | 
			
		||||
  /* ## Sections in RAM */
 | 
			
		||||
  /* ### Vector table */
 | 
			
		||||
  .vector_table ORIGIN(RAM) :
 | 
			
		||||
  {
 | 
			
		||||
    /* Initial Stack Pointer (SP) value */
 | 
			
		||||
    LONG(_stack_start);
 | 
			
		||||
 | 
			
		||||
    /* Reset vector */
 | 
			
		||||
    KEEP(*(.vector_table.reset_vector)); /* this is the `__RESET_VECTOR` symbol */
 | 
			
		||||
    __reset_vector = .;
 | 
			
		||||
 | 
			
		||||
    /* Exceptions */
 | 
			
		||||
    KEEP(*(.vector_table.exceptions)); /* this is the `__EXCEPTIONS` symbol */
 | 
			
		||||
    __eexceptions = .;
 | 
			
		||||
 | 
			
		||||
    /* Device specific interrupts */
 | 
			
		||||
    KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */
 | 
			
		||||
  } > RAM
 | 
			
		||||
 | 
			
		||||
  PROVIDE(_stext = ADDR(.vector_table) + SIZEOF(.vector_table));
 | 
			
		||||
 | 
			
		||||
  /* ### .text */
 | 
			
		||||
  .text _stext :
 | 
			
		||||
  {
 | 
			
		||||
    __stext = .;
 | 
			
		||||
    *(.Reset);
 | 
			
		||||
 | 
			
		||||
    *(.text .text.*);
 | 
			
		||||
 | 
			
		||||
    /* The HardFaultTrampoline uses the `b` instruction to enter `HardFault`,
 | 
			
		||||
       so must be placed close to it. */
 | 
			
		||||
    *(.HardFaultTrampoline);
 | 
			
		||||
    *(.HardFault.*);
 | 
			
		||||
 | 
			
		||||
    . = ALIGN(4); /* Pad .text to the alignment to workaround overlapping load section bug in old lld */
 | 
			
		||||
    __etext = .;
 | 
			
		||||
  } > RAM
 | 
			
		||||
 | 
			
		||||
  /* ### .rodata */
 | 
			
		||||
  .rodata : ALIGN(4)
 | 
			
		||||
  {
 | 
			
		||||
    . = ALIGN(4);
 | 
			
		||||
    __srodata = .;
 | 
			
		||||
    *(.rodata .rodata.*);
 | 
			
		||||
 | 
			
		||||
    /* 4-byte align the end (VMA) of this section.
 | 
			
		||||
       This is required by LLD to ensure the LMA of the following .data
 | 
			
		||||
       section will have the correct alignment. */
 | 
			
		||||
    . = ALIGN(4);
 | 
			
		||||
    __erodata = .;
 | 
			
		||||
  } > RAM
 | 
			
		||||
 | 
			
		||||
  /* ## Sections in RAM */
 | 
			
		||||
  /* ### .data */
 | 
			
		||||
  .data : ALIGN(4)
 | 
			
		||||
  {
 | 
			
		||||
    . = ALIGN(4);
 | 
			
		||||
    __sdata = .;
 | 
			
		||||
    __edata = .;
 | 
			
		||||
    *(.data .data.*);
 | 
			
		||||
    . = ALIGN(4); /* 4-byte align the end (VMA) of this section */
 | 
			
		||||
  } > RAM
 | 
			
		||||
  /* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to
 | 
			
		||||
   * use the .data loading mechanism by pushing __edata. Note: do not change
 | 
			
		||||
   * output region or load region in those user sections! */
 | 
			
		||||
  . = ALIGN(4);
 | 
			
		||||
 | 
			
		||||
  /* LMA of .data */
 | 
			
		||||
  __sidata = LOADADDR(.data);
 | 
			
		||||
 | 
			
		||||
  /* ### .gnu.sgstubs
 | 
			
		||||
     This section contains the TrustZone-M veneers put there by the Arm GNU linker. */
 | 
			
		||||
  /* Security Attribution Unit blocks must be 32 bytes aligned. */
 | 
			
		||||
  /* Note that this pads the RAM usage to 32 byte alignment. */
 | 
			
		||||
  .gnu.sgstubs : ALIGN(32)
 | 
			
		||||
  {
 | 
			
		||||
    . = ALIGN(32);
 | 
			
		||||
    __veneer_base = .;
 | 
			
		||||
    *(.gnu.sgstubs*)
 | 
			
		||||
    . = ALIGN(32);
 | 
			
		||||
    __veneer_limit = .;
 | 
			
		||||
  } > RAM
 | 
			
		||||
 | 
			
		||||
  /* ### .bss */
 | 
			
		||||
  .bss (NOLOAD) : ALIGN(4)
 | 
			
		||||
  {
 | 
			
		||||
    . = ALIGN(4);
 | 
			
		||||
    __sbss = .;
 | 
			
		||||
    *(.bss .bss.*);
 | 
			
		||||
    *(COMMON); /* Uninitialized C statics */
 | 
			
		||||
    . = ALIGN(4); /* 4-byte align the end (VMA) of this section */
 | 
			
		||||
  } > RAM
 | 
			
		||||
  /* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to
 | 
			
		||||
   * use the .bss zeroing mechanism by pushing __ebss. Note: do not change
 | 
			
		||||
   * output region or load region in those user sections! */
 | 
			
		||||
  . = ALIGN(4);
 | 
			
		||||
  __ebss = .;
 | 
			
		||||
 | 
			
		||||
  /* ### .uninit */
 | 
			
		||||
  .uninit (NOLOAD) : ALIGN(4)
 | 
			
		||||
  {
 | 
			
		||||
    . = ALIGN(4);
 | 
			
		||||
    __suninit = .;
 | 
			
		||||
    *(.uninit .uninit.*);
 | 
			
		||||
    . = ALIGN(4);
 | 
			
		||||
    __euninit = .;
 | 
			
		||||
  } > RAM
 | 
			
		||||
 | 
			
		||||
  /* Place the heap right after `.uninit` in RAM */
 | 
			
		||||
  PROVIDE(__sheap = __euninit);
 | 
			
		||||
 | 
			
		||||
  /* ## .got */
 | 
			
		||||
  /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in
 | 
			
		||||
     the input files and raise an error if relocatable code is found */
 | 
			
		||||
  .got (NOLOAD) :
 | 
			
		||||
  {
 | 
			
		||||
    KEEP(*(.got .got.*));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* ## Discarded sections */
 | 
			
		||||
  /DISCARD/ :
 | 
			
		||||
  {
 | 
			
		||||
    /* Unused exception related info that only wastes space */
 | 
			
		||||
    *(.ARM.exidx);
 | 
			
		||||
    *(.ARM.exidx.*);
 | 
			
		||||
    *(.ARM.extab.*);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Do not exceed this mark in the error messages below                                    | */
 | 
			
		||||
/* # Alignment checks */
 | 
			
		||||
ASSERT(ORIGIN(RAM) % 4 == 0, "
 | 
			
		||||
ERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned");
 | 
			
		||||
 | 
			
		||||
ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, "
 | 
			
		||||
BUG(cortex-m-rt): .data is not 4-byte aligned");
 | 
			
		||||
 | 
			
		||||
ASSERT(__sidata % 4 == 0, "
 | 
			
		||||
BUG(cortex-m-rt): the LMA of .data is not 4-byte aligned");
 | 
			
		||||
 | 
			
		||||
ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, "
 | 
			
		||||
BUG(cortex-m-rt): .bss is not 4-byte aligned");
 | 
			
		||||
 | 
			
		||||
ASSERT(__sheap % 4 == 0, "
 | 
			
		||||
BUG(cortex-m-rt): start of .heap is not 4-byte aligned");
 | 
			
		||||
 | 
			
		||||
/* # Position checks */
 | 
			
		||||
 | 
			
		||||
/* ## .vector_table */
 | 
			
		||||
ASSERT(__reset_vector == ADDR(.vector_table) + 0x8, "
 | 
			
		||||
BUG(cortex-m-rt): the reset vector is missing");
 | 
			
		||||
 | 
			
		||||
ASSERT(__eexceptions == ADDR(.vector_table) + 0x40, "
 | 
			
		||||
BUG(cortex-m-rt): the exception vectors are missing");
 | 
			
		||||
 | 
			
		||||
ASSERT(SIZEOF(.vector_table) > 0x40, "
 | 
			
		||||
ERROR(cortex-m-rt): The interrupt vectors are missing.
 | 
			
		||||
Possible solutions, from most likely to less likely:
 | 
			
		||||
- Link to a svd2rust generated device crate
 | 
			
		||||
- Check that you actually use the device/hal/bsp crate in your code
 | 
			
		||||
- Disable the 'device' feature of cortex-m-rt to build a generic application (a dependency
 | 
			
		||||
may be enabling it)
 | 
			
		||||
- Supply the interrupt handlers yourself. Check the documentation for details.");
 | 
			
		||||
 | 
			
		||||
/* ## .text */
 | 
			
		||||
ASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) <= _stext, "
 | 
			
		||||
ERROR(cortex-m-rt): The .text section can't be placed inside the .vector_table section
 | 
			
		||||
Set _stext to an address greater than the end of .vector_table (See output of `nm`)");
 | 
			
		||||
 | 
			
		||||
ASSERT(_stext + SIZEOF(.text) < ORIGIN(RAM) + LENGTH(RAM), "
 | 
			
		||||
ERROR(cortex-m-rt): The .text section must be placed inside the RAM memory.
 | 
			
		||||
Set _stext to an address smaller than 'ORIGIN(RAM) + LENGTH(RAM)'");
 | 
			
		||||
 | 
			
		||||
/* # Other checks */
 | 
			
		||||
ASSERT(SIZEOF(.got) == 0, "
 | 
			
		||||
ERROR(cortex-m-rt): .got section detected in the input object files
 | 
			
		||||
Dynamic relocations are not supported. If you are linking to C code compiled using
 | 
			
		||||
the 'cc' crate then modify your build script to compile the C code _without_
 | 
			
		||||
the -fPIC flag. See the documentation of the `cc::Build.pic` method for details.");
 | 
			
		||||
/* Do not exceed this mark in the error messages above                                    | */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Provides weak aliases (cf. PROVIDED) for device specific interrupt handlers */
 | 
			
		||||
/* This will usually be provided by a device crate generated using svd2rust (see `device.x`) */
 | 
			
		||||
INCLUDE device.x
 | 
			
		||||
							
								
								
									
										5
									
								
								tests/nrf/memory.x
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/nrf/memory.x
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
MEMORY
 | 
			
		||||
{
 | 
			
		||||
  FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
 | 
			
		||||
  RAM : ORIGIN = 0x20000000, LENGTH = 256K
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										74
									
								
								tests/nrf/src/bin/buffered_uart.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								tests/nrf/src/bin/buffered_uart.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![no_main]
 | 
			
		||||
#![feature(type_alias_impl_trait)]
 | 
			
		||||
 | 
			
		||||
use defmt::{assert_eq, *};
 | 
			
		||||
use embassy_executor::Spawner;
 | 
			
		||||
use embassy_futures::join::join;
 | 
			
		||||
use embassy_nrf::buffered_uarte::BufferedUarte;
 | 
			
		||||
use embassy_nrf::{interrupt, uarte};
 | 
			
		||||
use {defmt_rtt as _, panic_probe as _};
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::main]
 | 
			
		||||
async fn main(_spawner: Spawner) {
 | 
			
		||||
    let p = embassy_nrf::init(Default::default());
 | 
			
		||||
    let mut config = uarte::Config::default();
 | 
			
		||||
    config.parity = uarte::Parity::EXCLUDED;
 | 
			
		||||
    config.baudrate = uarte::Baudrate::BAUD1M;
 | 
			
		||||
 | 
			
		||||
    let mut tx_buffer = [0u8; 1024];
 | 
			
		||||
    let mut rx_buffer = [0u8; 1024];
 | 
			
		||||
 | 
			
		||||
    let mut u = BufferedUarte::new(
 | 
			
		||||
        p.UARTE0,
 | 
			
		||||
        p.TIMER0,
 | 
			
		||||
        p.PPI_CH0,
 | 
			
		||||
        p.PPI_CH1,
 | 
			
		||||
        p.PPI_GROUP0,
 | 
			
		||||
        interrupt::take!(UARTE0_UART0),
 | 
			
		||||
        p.P1_03,
 | 
			
		||||
        p.P1_02,
 | 
			
		||||
        config.clone(),
 | 
			
		||||
        &mut rx_buffer,
 | 
			
		||||
        &mut tx_buffer,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    info!("uarte initialized!");
 | 
			
		||||
 | 
			
		||||
    let (mut rx, mut tx) = u.split();
 | 
			
		||||
 | 
			
		||||
    const COUNT: usize = 40_000;
 | 
			
		||||
 | 
			
		||||
    let tx_fut = async {
 | 
			
		||||
        let mut tx_buf = [0; 215];
 | 
			
		||||
        let mut i = 0;
 | 
			
		||||
        while i < COUNT {
 | 
			
		||||
            let n = tx_buf.len().min(COUNT - i);
 | 
			
		||||
            let tx_buf = &mut tx_buf[..n];
 | 
			
		||||
            for (j, b) in tx_buf.iter_mut().enumerate() {
 | 
			
		||||
                *b = (i + j) as u8;
 | 
			
		||||
            }
 | 
			
		||||
            let n = unwrap!(tx.write(tx_buf).await);
 | 
			
		||||
            i += n;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    let rx_fut = async {
 | 
			
		||||
        let mut i = 0;
 | 
			
		||||
        while i < COUNT {
 | 
			
		||||
            let buf = unwrap!(rx.fill_buf().await);
 | 
			
		||||
 | 
			
		||||
            for &b in buf {
 | 
			
		||||
                assert_eq!(b, i as u8);
 | 
			
		||||
                i = i + 1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let n = buf.len();
 | 
			
		||||
            rx.consume(n);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    join(rx_fut, tx_fut).await;
 | 
			
		||||
 | 
			
		||||
    info!("Test OK");
 | 
			
		||||
    cortex_m::asm::bkpt();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										86
									
								
								tests/nrf/src/bin/buffered_uart_spam.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								tests/nrf/src/bin/buffered_uart_spam.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![no_main]
 | 
			
		||||
#![feature(type_alias_impl_trait)]
 | 
			
		||||
 | 
			
		||||
use core::mem;
 | 
			
		||||
use core::ptr::NonNull;
 | 
			
		||||
 | 
			
		||||
use defmt::{assert_eq, *};
 | 
			
		||||
use embassy_executor::Spawner;
 | 
			
		||||
use embassy_nrf::buffered_uarte::BufferedUarte;
 | 
			
		||||
use embassy_nrf::gpio::{Level, Output, OutputDrive};
 | 
			
		||||
use embassy_nrf::ppi::{Event, Ppi, Task};
 | 
			
		||||
use embassy_nrf::uarte::Uarte;
 | 
			
		||||
use embassy_nrf::{interrupt, pac, uarte};
 | 
			
		||||
use embassy_time::{Duration, Timer};
 | 
			
		||||
use {defmt_rtt as _, panic_probe as _};
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::main]
 | 
			
		||||
async fn main(_spawner: Spawner) {
 | 
			
		||||
    let mut p = embassy_nrf::init(Default::default());
 | 
			
		||||
    let mut config = uarte::Config::default();
 | 
			
		||||
    config.parity = uarte::Parity::EXCLUDED;
 | 
			
		||||
    config.baudrate = uarte::Baudrate::BAUD1M;
 | 
			
		||||
 | 
			
		||||
    let mut tx_buffer = [0u8; 1024];
 | 
			
		||||
    let mut rx_buffer = [0u8; 1024];
 | 
			
		||||
 | 
			
		||||
    mem::forget(Output::new(&mut p.P1_02, Level::High, OutputDrive::Standard));
 | 
			
		||||
 | 
			
		||||
    let mut u = BufferedUarte::new(
 | 
			
		||||
        p.UARTE0,
 | 
			
		||||
        p.TIMER0,
 | 
			
		||||
        p.PPI_CH0,
 | 
			
		||||
        p.PPI_CH1,
 | 
			
		||||
        p.PPI_GROUP0,
 | 
			
		||||
        interrupt::take!(UARTE0_UART0),
 | 
			
		||||
        p.P1_03,
 | 
			
		||||
        p.P1_04,
 | 
			
		||||
        config.clone(),
 | 
			
		||||
        &mut rx_buffer,
 | 
			
		||||
        &mut tx_buffer,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    info!("uarte initialized!");
 | 
			
		||||
 | 
			
		||||
    // uarte needs some quiet time to start rxing properly.
 | 
			
		||||
    Timer::after(Duration::from_millis(10)).await;
 | 
			
		||||
 | 
			
		||||
    // Tx spam in a loop.
 | 
			
		||||
    const NSPAM: usize = 17;
 | 
			
		||||
    static mut TX_BUF: [u8; NSPAM] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
 | 
			
		||||
    let _spam = Uarte::new(p.UARTE1, interrupt::take!(UARTE1), p.P1_01, p.P1_02, config.clone());
 | 
			
		||||
    let spam_peri: pac::UARTE1 = unsafe { mem::transmute(()) };
 | 
			
		||||
    let event = unsafe { Event::new_unchecked(NonNull::new_unchecked(&spam_peri.events_endtx as *const _ as _)) };
 | 
			
		||||
    let task = unsafe { Task::new_unchecked(NonNull::new_unchecked(&spam_peri.tasks_starttx as *const _ as _)) };
 | 
			
		||||
    let mut spam_ppi = Ppi::new_one_to_one(p.PPI_CH2, event, task);
 | 
			
		||||
    spam_ppi.enable();
 | 
			
		||||
    let p = unsafe { TX_BUF.as_mut_ptr() };
 | 
			
		||||
    spam_peri.txd.ptr.write(|w| unsafe { w.ptr().bits(p as u32) });
 | 
			
		||||
    spam_peri.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(NSPAM as _) });
 | 
			
		||||
    spam_peri.tasks_starttx.write(|w| unsafe { w.bits(1) });
 | 
			
		||||
 | 
			
		||||
    let mut i = 0;
 | 
			
		||||
    let mut total = 0;
 | 
			
		||||
    while total < 256 * 1024 {
 | 
			
		||||
        let buf = unwrap!(u.fill_buf().await);
 | 
			
		||||
        //info!("rx {}", buf);
 | 
			
		||||
 | 
			
		||||
        for &b in buf {
 | 
			
		||||
            assert_eq!(b, unsafe { TX_BUF[i] });
 | 
			
		||||
 | 
			
		||||
            i = i + 1;
 | 
			
		||||
            if i == NSPAM {
 | 
			
		||||
                i = 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again
 | 
			
		||||
        let n = buf.len();
 | 
			
		||||
        u.consume(n);
 | 
			
		||||
        total += n;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    info!("Test OK");
 | 
			
		||||
    cortex_m::asm::bkpt();
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user