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:
		
						commit
						d91efe3e62
					
				
							
								
								
									
										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 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/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/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
 | 
					    $BUILD_EXTRA
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,5 @@
 | 
				
			|||||||
//! Async buffered UART driver.
 | 
					//! 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
 | 
					//! 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
 | 
					//! 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
 | 
					//! 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.
 | 
					//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use core::cell::RefCell;
 | 
					 | 
				
			||||||
use core::cmp::min;
 | 
					use core::cmp::min;
 | 
				
			||||||
use core::future::poll_fn;
 | 
					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 core::task::Poll;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
 | 
					use embassy_cortex_m::interrupt::Interrupt;
 | 
				
			||||||
use embassy_hal_common::ring_buffer::RingBuffer;
 | 
					use embassy_hal_common::atomic_ring_buffer::RingBuffer;
 | 
				
			||||||
use embassy_hal_common::{into_ref, PeripheralRef};
 | 
					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
 | 
					// 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};
 | 
					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::interrupt::InterruptExt;
 | 
				
			||||||
use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};
 | 
					use crate::ppi::{
 | 
				
			||||||
use crate::timer::{Frequency, Instance as TimerInstance, Timer};
 | 
					    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::uarte::{apply_workaround_for_enable_anomaly, Config, Instance as UarteInstance};
 | 
				
			||||||
use crate::{pac, Peripheral};
 | 
					use crate::{pac, Peripheral};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Copy, Clone, Debug, PartialEq)]
 | 
					mod sealed {
 | 
				
			||||||
enum RxState {
 | 
					    use super::*;
 | 
				
			||||||
    Idle,
 | 
					 | 
				
			||||||
    Receiving,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Copy, Clone, Debug, PartialEq)]
 | 
					    pub struct State {
 | 
				
			||||||
enum TxState {
 | 
					        pub tx_waker: AtomicWaker,
 | 
				
			||||||
    Idle,
 | 
					        pub tx_buf: RingBuffer,
 | 
				
			||||||
    Transmitting(usize),
 | 
					        pub tx_count: AtomicUsize,
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// A type for storing the state of the UARTE peripheral that can be stored in a static.
 | 
					        pub rx_waker: AtomicWaker,
 | 
				
			||||||
pub struct State<'d, U: UarteInstance, T: TimerInstance>(StateStorage<StateInner<'d, U, T>>);
 | 
					        pub rx_buf: RingBuffer,
 | 
				
			||||||
impl<'d, U: UarteInstance, T: TimerInstance> State<'d, U, T> {
 | 
					        pub rx_bufs: AtomicU8,
 | 
				
			||||||
    /// Create an instance for storing UARTE peripheral state.
 | 
					        pub rx_ppi_ch: AtomicU8,
 | 
				
			||||||
    pub fn new() -> Self {
 | 
					 | 
				
			||||||
        Self(StateStorage::new())
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct StateInner<'d, U: UarteInstance, T: TimerInstance> {
 | 
					/// UART error.
 | 
				
			||||||
    _peri: PeripheralRef<'d, U>,
 | 
					#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | 
				
			||||||
    timer: Timer<'d, T>,
 | 
					#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
				
			||||||
    _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>,
 | 
					#[non_exhaustive]
 | 
				
			||||||
    _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>,
 | 
					pub enum Error {
 | 
				
			||||||
 | 
					    // No errors for now
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rx: RingBuffer<'d>,
 | 
					pub(crate) use sealed::State;
 | 
				
			||||||
    rx_state: RxState,
 | 
					 | 
				
			||||||
    rx_waker: WakerRegistration,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tx: RingBuffer<'d>,
 | 
					impl State {
 | 
				
			||||||
    tx_state: TxState,
 | 
					    pub(crate) const fn new() -> Self {
 | 
				
			||||||
    tx_waker: WakerRegistration,
 | 
					        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.
 | 
					/// Buffered UARTE driver.
 | 
				
			||||||
pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> {
 | 
					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> Unpin for BufferedUarte<'d, U, T> {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, U: UarteInstance, T: TimerInstance> 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
 | 
					    /// Panics if `rx_buffer.len()` is odd.
 | 
				
			||||||
    /// can return early if there is no data to receive.
 | 
					 | 
				
			||||||
    pub fn new(
 | 
					    pub fn new(
 | 
				
			||||||
        state: &'d mut State<'d, U, T>,
 | 
					        uarte: impl Peripheral<P = U> + 'd,
 | 
				
			||||||
        peri: impl Peripheral<P = U> + 'd,
 | 
					 | 
				
			||||||
        timer: impl Peripheral<P = T> + 'd,
 | 
					        timer: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
        ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
 | 
					        ppi_ch1: impl Peripheral<P = impl ConfigurableChannel> + 'd,
 | 
				
			||||||
        ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + '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,
 | 
					        irq: impl Peripheral<P = U::Interrupt> + 'd,
 | 
				
			||||||
        rxd: impl Peripheral<P = impl GpioPin> + 'd,
 | 
					        rxd: impl Peripheral<P = impl GpioPin> + 'd,
 | 
				
			||||||
        txd: 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],
 | 
					        rx_buffer: &'d mut [u8],
 | 
				
			||||||
        tx_buffer: &'d mut [u8],
 | 
					        tx_buffer: &'d mut [u8],
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> 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 r = U::regs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut timer = Timer::new(timer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        rxd.conf().write(|w| w.input().connect().drive().h0h1());
 | 
					        rxd.conf().write(|w| w.input().connect().drive().h0h1());
 | 
				
			||||||
        r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
 | 
					        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());
 | 
					        txd.conf().write(|w| w.dir().output().drive().h0h1());
 | 
				
			||||||
        r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) });
 | 
					        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()) });
 | 
					        r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        rts.set_high();
 | 
					        if let Some(pin) = &rts {
 | 
				
			||||||
        rts.conf().write(|w| w.dir().output().drive().h0h1());
 | 
					            pin.set_high();
 | 
				
			||||||
 | 
					            pin.conf().write(|w| w.dir().output().drive().h0h1());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) });
 | 
					        r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        r.baudrate.write(|w| w.baudrate().variant(config.baudrate));
 | 
					        // Initialize state
 | 
				
			||||||
        r.config.write(|w| w.parity().variant(config.parity));
 | 
					        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
 | 
					        // Configure
 | 
				
			||||||
        r.config.write(|w| {
 | 
					        r.config.write(|w| {
 | 
				
			||||||
            w.hwfc().bit(true);
 | 
					            w.hwfc().bit(false);
 | 
				
			||||||
            w.parity().variant(config.parity);
 | 
					            w.parity().variant(config.parity);
 | 
				
			||||||
            w
 | 
					            w
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        r.baudrate.write(|w| w.baudrate().variant(config.baudrate));
 | 
					        r.baudrate.write(|w| w.baudrate().variant(config.baudrate));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Enable interrupts
 | 
					        // clear errors
 | 
				
			||||||
        r.intenset.write(|w| w.endrx().set().endtx().set());
 | 
					        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.
 | 
					        r.events_rxstarted.reset();
 | 
				
			||||||
        irq.disable();
 | 
					        r.events_txstarted.reset();
 | 
				
			||||||
        irq.pend();
 | 
					        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
 | 
					        // Enable UARTE instance
 | 
				
			||||||
        apply_workaround_for_enable_anomaly(&r);
 | 
					        apply_workaround_for_enable_anomaly(&r);
 | 
				
			||||||
        r.enable.write(|w| w.enable().enabled());
 | 
					        r.enable.write(|w| w.enable().enabled());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // BAUDRATE register values are `baudrate * 2^32 / 16000000`
 | 
					        // Configure byte counter.
 | 
				
			||||||
        // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values
 | 
					        let mut timer = Timer::new_counter(timer);
 | 
				
			||||||
        //
 | 
					        timer.cc(1).write(rx_buffer.len() as u32 * 2);
 | 
				
			||||||
        // We want to stop RX if line is idle for 2 bytes worth of time
 | 
					        timer.cc(1).short_compare_clear();
 | 
				
			||||||
        // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit)
 | 
					        timer.clear();
 | 
				
			||||||
        // This gives us the amount of 16M ticks for 20 bits.
 | 
					        timer.start();
 | 
				
			||||||
        let timeout = 0x8000_0000 / (config.baudrate as u32 / 40);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        timer.set_frequency(Frequency::F16MHz);
 | 
					        let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_rxdrdy), timer.task_count());
 | 
				
			||||||
        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(),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        ppi_ch1.enable();
 | 
					        ppi_ch1.enable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut ppi_ch2 = Ppi::new_one_to_one(
 | 
					        s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed);
 | 
				
			||||||
            ppi_ch2.map_into(),
 | 
					        let mut ppi_group = PpiGroup::new(ppi_group);
 | 
				
			||||||
            timer.cc(0).event_compare(),
 | 
					        let mut ppi_ch2 = Ppi::new_one_to_two(
 | 
				
			||||||
            Task::from_reg(&r.tasks_stoprx),
 | 
					            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 {
 | 
					        Self {
 | 
				
			||||||
            inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner {
 | 
					            _peri: peri,
 | 
				
			||||||
                _peri: peri,
 | 
					            timer,
 | 
				
			||||||
                timer,
 | 
					            _ppi_ch1: ppi_ch1,
 | 
				
			||||||
                _ppi_ch1: ppi_ch1,
 | 
					            _ppi_ch2: ppi_ch2,
 | 
				
			||||||
                _ppi_ch2: ppi_ch2,
 | 
					            _ppi_group: ppi_group,
 | 
				
			||||||
 | 
					 | 
				
			||||||
                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(),
 | 
					 | 
				
			||||||
            })),
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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.
 | 
					    /// Adjust the baud rate to the provided value.
 | 
				
			||||||
    pub fn set_baudrate(&mut self, baudrate: Baudrate) {
 | 
					    pub fn set_baudrate(&mut self, baudrate: Baudrate) {
 | 
				
			||||||
        self.inner.borrow_mut().with(|state| {
 | 
					        let r = U::regs();
 | 
				
			||||||
            let r = U::regs();
 | 
					        r.baudrate.write(|w| w.baudrate().variant(baudrate));
 | 
				
			||||||
 | 
					 | 
				
			||||||
            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));
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Split the UART in reader and writer parts.
 | 
					    /// 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 })
 | 
					        (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| {
 | 
					        poll_fn(move |cx| {
 | 
				
			||||||
            let mut do_pend = false;
 | 
					            //trace!("poll_write: {:?}", buf.len());
 | 
				
			||||||
            let mut inner = self.inner.borrow_mut();
 | 
					            let s = U::buffered_state();
 | 
				
			||||||
            let res = inner.with(|state| {
 | 
					            let mut tx = unsafe { s.tx_buf.writer() };
 | 
				
			||||||
                compiler_fence(Ordering::SeqCst);
 | 
					 | 
				
			||||||
                trace!("poll_read");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // We have data ready in buffer? Return it.
 | 
					            let tx_buf = tx.push_slice();
 | 
				
			||||||
                let data = state.rx.pop_buf();
 | 
					            if tx_buf.is_empty() {
 | 
				
			||||||
                if !data.is_empty() {
 | 
					                //trace!("poll_write: pending");
 | 
				
			||||||
                    trace!("  got {:?} {:?}", data.as_ptr() as u32, data.len());
 | 
					                s.tx_waker.register(cx.waker());
 | 
				
			||||||
                    let len = data.len().min(buf.len());
 | 
					                return Poll::Pending;
 | 
				
			||||||
                    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();
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            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
 | 
					        .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| {
 | 
					        poll_fn(move |cx| {
 | 
				
			||||||
            let mut inner = self.inner.borrow_mut();
 | 
					            //trace!("poll_flush");
 | 
				
			||||||
            let res = inner.with(|state| {
 | 
					            let s = U::buffered_state();
 | 
				
			||||||
                trace!("poll_write: {:?}", buf.len());
 | 
					            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();
 | 
					            Poll::Ready(Ok(()))
 | 
				
			||||||
                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
 | 
					 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .await
 | 
					        .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| {
 | 
					        poll_fn(move |cx| {
 | 
				
			||||||
            self.inner.borrow_mut().with(|state| {
 | 
					            compiler_fence(Ordering::SeqCst);
 | 
				
			||||||
                trace!("poll_flush");
 | 
					            //trace!("poll_read");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if !state.tx.is_empty() {
 | 
					            let r = U::regs();
 | 
				
			||||||
                    trace!("poll_flush: pending");
 | 
					            let s = U::buffered_state();
 | 
				
			||||||
                    state.tx_waker.register(cx.waker());
 | 
					 | 
				
			||||||
                    return Poll::Pending;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Poll::Ready(Ok(()))
 | 
					            // 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;
 | 
				
			||||||
        .await
 | 
					            //trace!("  rxdrdy count = {:?}", end);
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], core::convert::Infallible> {
 | 
					            // We've set a compare channel that resets the counter to 0 when it reaches `len*2`.
 | 
				
			||||||
        poll_fn(move |cx| {
 | 
					            // However, it's unclear if that's instant, or there's a small window where you can
 | 
				
			||||||
            self.inner.borrow_mut().with(|state| {
 | 
					            // still read `len()*2`.
 | 
				
			||||||
                compiler_fence(Ordering::SeqCst);
 | 
					            // This could happen if in one clock cycle the counter is updated, and in the next the
 | 
				
			||||||
                trace!("fill_buf");
 | 
					            // 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.
 | 
					            // This logic mirrors `atomic_ring_buffer::Reader::pop_buf()`
 | 
				
			||||||
                let buf = state.rx.pop_buf();
 | 
					            let mut start = s.rx_buf.start.load(Ordering::Relaxed);
 | 
				
			||||||
                if !buf.is_empty() {
 | 
					            let len = s.rx_buf.len();
 | 
				
			||||||
                    trace!("  got {:?} {:?}", buf.as_ptr() as u32, buf.len());
 | 
					            if start == end {
 | 
				
			||||||
                    let buf: &[u8] = buf;
 | 
					                //trace!("  empty");
 | 
				
			||||||
                    // Safety: buffer lives as long as uart
 | 
					                s.rx_waker.register(cx.waker());
 | 
				
			||||||
                    let buf: &[u8] = unsafe { core::mem::transmute(buf) };
 | 
					                r.intenset.write(|w| w.rxdrdy().set_bit());
 | 
				
			||||||
                    return Poll::Ready(Ok(buf));
 | 
					                return Poll::Pending;
 | 
				
			||||||
                }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                trace!("  empty");
 | 
					            if start >= len {
 | 
				
			||||||
                state.rx_waker.register(cx.waker());
 | 
					                start -= len
 | 
				
			||||||
                Poll::<Result<&[u8], core::convert::Infallible>>::Pending
 | 
					            }
 | 
				
			||||||
            })
 | 
					            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
 | 
					        .await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn inner_consume(&self, amt: usize) {
 | 
					    fn inner_consume(&self, amt: usize) {
 | 
				
			||||||
        let mut inner = self.inner.borrow_mut();
 | 
					        if amt == 0 {
 | 
				
			||||||
        let signal = inner.with(|state| {
 | 
					            return;
 | 
				
			||||||
            let full = state.rx.is_full();
 | 
					 | 
				
			||||||
            state.rx.pop(amt);
 | 
					 | 
				
			||||||
            full
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        if signal {
 | 
					 | 
				
			||||||
            inner.pend();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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>,
 | 
					    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.
 | 
					/// Writer part of the buffered UARTE driver.
 | 
				
			||||||
pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> {
 | 
					pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> {
 | 
				
			||||||
    inner: &'u BufferedUarte<'d, U, T>,
 | 
					    inner: &'u BufferedUarte<'d, U, T>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> {
 | 
					impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'u, 'd, U, T> {
 | 
				
			||||||
    type Error = core::convert::Infallible;
 | 
					    /// 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> {
 | 
				
			||||||
 | 
					 | 
				
			||||||
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> {
 | 
					 | 
				
			||||||
        self.inner.inner_read(buf).await
 | 
					        self.inner.inner_read(buf).await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> {
 | 
					    /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.
 | 
				
			||||||
    async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
 | 
					    pub async fn fill_buf(&mut self) -> Result<&[u8], 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
 | 
					        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)
 | 
					        self.inner.inner_consume(amt)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> {
 | 
					#[cfg(feature = "nightly")]
 | 
				
			||||||
    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
 | 
					mod _embedded_io {
 | 
				
			||||||
        self.inner_write(buf).await
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl embedded_io::Error for Error {
 | 
				
			||||||
 | 
					        fn kind(&self) -> embedded_io::ErrorKind {
 | 
				
			||||||
 | 
					            match *self {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn flush(&mut self) -> Result<(), Self::Error> {
 | 
					    impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> {
 | 
				
			||||||
        self.inner_flush().await
 | 
					        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> {
 | 
					impl<'a, U: UarteInstance, T: TimerInstance> Drop for BufferedUarte<'a, 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> {
 | 
					 | 
				
			||||||
    fn drop(&mut self) {
 | 
					    fn drop(&mut self) {
 | 
				
			||||||
        let r = U::regs();
 | 
					        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.txd.read().bits());
 | 
				
			||||||
        gpio::deconfigure_pin(r.psel.rts.read().bits());
 | 
					        gpio::deconfigure_pin(r.psel.rts.read().bits());
 | 
				
			||||||
        gpio::deconfigure_pin(r.psel.cts.read().bits());
 | 
					        gpio::deconfigure_pin(r.psel.cts.read().bits());
 | 
				
			||||||
    }
 | 
					
 | 
				
			||||||
}
 | 
					        let s = U::buffered_state();
 | 
				
			||||||
 | 
					        unsafe {
 | 
				
			||||||
impl<'a, U: UarteInstance, T: TimerInstance> PeripheralState for StateInner<'a, U, T> {
 | 
					            s.rx_buf.deinit();
 | 
				
			||||||
    type Interrupt = U::Interrupt;
 | 
					            s.tx_buf.deinit();
 | 
				
			||||||
    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");
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -37,7 +37,6 @@ pub(crate) mod util;
 | 
				
			|||||||
#[cfg(feature = "_time-driver")]
 | 
					#[cfg(feature = "_time-driver")]
 | 
				
			||||||
mod time_driver;
 | 
					mod time_driver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "nightly")]
 | 
					 | 
				
			||||||
pub mod buffered_uarte;
 | 
					pub mod buffered_uarte;
 | 
				
			||||||
pub mod gpio;
 | 
					pub mod gpio;
 | 
				
			||||||
#[cfg(feature = "gpiote")]
 | 
					#[cfg(feature = "gpiote")]
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ use crate::{pac, Peripheral};
 | 
				
			|||||||
const DPPI_ENABLE_BIT: u32 = 0x8000_0000;
 | 
					const DPPI_ENABLE_BIT: u32 = 0x8000_0000;
 | 
				
			||||||
const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF;
 | 
					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() }
 | 
					    unsafe { &*pac::DPPIC::ptr() }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -17,16 +17,16 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use core::ptr::NonNull;
 | 
					use core::ptr::NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use embassy_hal_common::{impl_peripheral, PeripheralRef};
 | 
					use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{peripherals, Peripheral};
 | 
					use crate::{peripherals, Peripheral};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "_dppi")]
 | 
					#[cfg_attr(feature = "_dppi", path = "dppi.rs")]
 | 
				
			||||||
mod dppi;
 | 
					#[cfg_attr(feature = "_ppi", path = "ppi.rs")]
 | 
				
			||||||
#[cfg(feature = "_ppi")]
 | 
					mod _version;
 | 
				
			||||||
mod ppi;
 | 
					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> {
 | 
					pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
 | 
				
			||||||
    ch: PeripheralRef<'d, C>,
 | 
					    ch: PeripheralRef<'d, C>,
 | 
				
			||||||
    #[cfg(feature = "_dppi")]
 | 
					    #[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],
 | 
					    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")]
 | 
					#[cfg(feature = "_dppi")]
 | 
				
			||||||
const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
 | 
					const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -112,7 +194,7 @@ pub(crate) mod sealed {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Interface for PPI channels.
 | 
					/// 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
 | 
					    /// Returns the number of the channel
 | 
				
			||||||
    fn number(&self) -> usize;
 | 
					    fn number(&self) -> usize;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -130,7 +212,7 @@ pub trait StaticChannel: Channel + Into<AnyStaticChannel> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Interface for a group of PPI channels.
 | 
					/// 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.
 | 
					    /// Returns the number of the group.
 | 
				
			||||||
    fn number(&self) -> usize;
 | 
					    fn number(&self) -> usize;
 | 
				
			||||||
    /// Convert into a type erased group.
 | 
					    /// Convert into a type erased group.
 | 
				
			||||||
@ -248,6 +330,12 @@ macro_rules! impl_group {
 | 
				
			|||||||
                $number
 | 
					                $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() }
 | 
					    unsafe { &*pac::PPI::ptr() }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -132,7 +132,21 @@ impl<'d, T: Instance> Timer<'d, T, Awaitable> {
 | 
				
			|||||||
        irq.unpend();
 | 
					        irq.unpend();
 | 
				
			||||||
        irq.enable();
 | 
					        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
 | 
					    /// This can be useful for triggering tasks via PPI
 | 
				
			||||||
    /// `Uarte` uses this internally.
 | 
					    /// `Uarte` uses this internally.
 | 
				
			||||||
    pub fn new(timer: impl Peripheral<P = T> + 'd) -> Self {
 | 
					    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.
 | 
					    /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// This is used by the public constructors.
 | 
					    /// 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);
 | 
					        into_ref!(timer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let regs = T::regs();
 | 
					        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.
 | 
					        // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification.
 | 
				
			||||||
        this.stop();
 | 
					        this.stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Set the instance to timer mode.
 | 
					        if is_counter {
 | 
				
			||||||
        regs.mode.write(|w| w.mode().timer());
 | 
					            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.
 | 
					        // Make the counter's max value as high as possible.
 | 
				
			||||||
        // TODO: is there a reason someone would want to set this lower?
 | 
					        // 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)
 | 
					        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.
 | 
					    /// Change the timer's frequency.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// This will stop the timer if it isn't already stopped,
 | 
					    /// This will stop the timer if it isn't already stopped,
 | 
				
			||||||
 | 
				
			|||||||
@ -883,6 +883,7 @@ pub(crate) mod sealed {
 | 
				
			|||||||
    pub trait Instance {
 | 
					    pub trait Instance {
 | 
				
			||||||
        fn regs() -> &'static pac::uarte0::RegisterBlock;
 | 
					        fn regs() -> &'static pac::uarte0::RegisterBlock;
 | 
				
			||||||
        fn state() -> &'static State;
 | 
					        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();
 | 
					                static STATE: crate::uarte::sealed::State = crate::uarte::sealed::State::new();
 | 
				
			||||||
                &STATE
 | 
					                &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 {
 | 
					        impl crate::uarte::Instance for peripherals::$type {
 | 
				
			||||||
            type Interrupt = crate::interrupt::$irq;
 | 
					            type Interrupt = crate::interrupt::$irq;
 | 
				
			||||||
 | 
				
			|||||||
@ -4,10 +4,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use defmt::*;
 | 
					use defmt::*;
 | 
				
			||||||
use embassy_executor::Spawner;
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
use embassy_nrf::buffered_uarte::{BufferedUarte, State};
 | 
					use embassy_nrf::buffered_uarte::BufferedUarte;
 | 
				
			||||||
use embassy_nrf::{interrupt, uarte};
 | 
					use embassy_nrf::{interrupt, uarte};
 | 
				
			||||||
use embedded_io::asynch::{BufRead, Write};
 | 
					use embedded_io::asynch::Write;
 | 
				
			||||||
use futures::pin_mut;
 | 
					 | 
				
			||||||
use {defmt_rtt as _, panic_probe as _};
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[embassy_executor::main]
 | 
					#[embassy_executor::main]
 | 
				
			||||||
@ -21,24 +20,19 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
    let mut rx_buffer = [0u8; 4096];
 | 
					    let mut rx_buffer = [0u8; 4096];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let irq = interrupt::take!(UARTE0_UART0);
 | 
					    let irq = interrupt::take!(UARTE0_UART0);
 | 
				
			||||||
    let mut state = State::new();
 | 
					    let mut u = BufferedUarte::new(
 | 
				
			||||||
    // Please note - important to have hardware flow control (https://github.com/embassy-rs/embassy/issues/536)
 | 
					 | 
				
			||||||
    let u = BufferedUarte::new(
 | 
					 | 
				
			||||||
        &mut state,
 | 
					 | 
				
			||||||
        p.UARTE0,
 | 
					        p.UARTE0,
 | 
				
			||||||
        p.TIMER0,
 | 
					        p.TIMER0,
 | 
				
			||||||
        p.PPI_CH0,
 | 
					        p.PPI_CH0,
 | 
				
			||||||
        p.PPI_CH1,
 | 
					        p.PPI_CH1,
 | 
				
			||||||
 | 
					        p.PPI_GROUP0,
 | 
				
			||||||
        irq,
 | 
					        irq,
 | 
				
			||||||
        p.P0_08,
 | 
					        p.P0_08,
 | 
				
			||||||
        p.P0_06,
 | 
					        p.P0_06,
 | 
				
			||||||
        p.P0_07,
 | 
					 | 
				
			||||||
        p.P0_05,
 | 
					 | 
				
			||||||
        config,
 | 
					        config,
 | 
				
			||||||
        &mut rx_buffer,
 | 
					        &mut rx_buffer,
 | 
				
			||||||
        &mut tx_buffer,
 | 
					        &mut tx_buffer,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    pin_mut!(u);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    info!("uarte initialized!");
 | 
					    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();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user