From de5760cc81a00966c61d668c41f6e3e4709f0283 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 14 Oct 2025 13:23:50 +0200 Subject: feat: improve nrf54 support using new nrf-pac * Update nrf-pac to version that modifies nrf52 register layout to match nrf54 to reduce the amount of cfg needed for nrf54 support. * Make the following peripherals available on nrf54: twim, twis, spim, spis, uart, buffered uarte, dppi, gpiote, pwm, saadc * Add examples tested on the nrf54 dk Some code is based on or copied from other pull requests, modified to match the new nrf-pac layout. Co-authored-by: Dmitry Tarnyagin --- embassy-net-nrf91/CHANGELOG.md | 2 + embassy-net-nrf91/Cargo.toml | 2 +- embassy-nrf/CHANGELOG.md | 2 + embassy-nrf/Cargo.toml | 21 +- embassy-nrf/src/buffered_uarte.rs | 951 -------------------------- embassy-nrf/src/buffered_uarte/mod.rs | 14 + embassy-nrf/src/buffered_uarte/v1.rs | 951 ++++++++++++++++++++++++++ embassy-nrf/src/buffered_uarte/v2.rs | 687 +++++++++++++++++++ embassy-nrf/src/chips/nrf51.rs | 5 + embassy-nrf/src/chips/nrf52805.rs | 51 +- embassy-nrf/src/chips/nrf52810.rs | 71 +- embassy-nrf/src/chips/nrf52811.rs | 71 +- embassy-nrf/src/chips/nrf52820.rs | 71 +- embassy-nrf/src/chips/nrf52832.rs | 71 +- embassy-nrf/src/chips/nrf52833.rs | 71 +- embassy-nrf/src/chips/nrf52840.rs | 71 +- embassy-nrf/src/chips/nrf5340_app.rs | 71 +- embassy-nrf/src/chips/nrf5340_net.rs | 71 +- embassy-nrf/src/chips/nrf54l15_app.rs | 208 +++++- embassy-nrf/src/chips/nrf9120.rs | 39 +- embassy-nrf/src/chips/nrf9160.rs | 39 +- embassy-nrf/src/gpio.rs | 2 - embassy-nrf/src/gpiote.rs | 431 +++++++++--- embassy-nrf/src/lib.rs | 12 - embassy-nrf/src/ppi/dppi.rs | 11 +- embassy-nrf/src/ppi/mod.rs | 119 +++- embassy-nrf/src/pwm.rs | 59 +- embassy-nrf/src/saadc.rs | 247 ++++++- embassy-nrf/src/spim.rs | 163 ++++- embassy-nrf/src/spis.rs | 16 +- embassy-nrf/src/twim.rs | 44 +- embassy-nrf/src/twis.rs | 44 +- embassy-nrf/src/uarte.rs | 162 +++-- examples/nrf52840/src/bin/egu.rs | 15 +- examples/nrf52840/src/bin/gpiote_channel.rs | 26 +- examples/nrf52840/src/bin/ppi.rs | 34 +- examples/nrf52840/src/bin/pwm_sequence_ppi.rs | 14 +- examples/nrf5340/src/bin/gpiote_channel.rs | 26 +- examples/nrf54l15/Cargo.toml | 5 + examples/nrf54l15/src/bin/buffered_uart.rs | 49 ++ examples/nrf54l15/src/bin/gpiote_channel.rs | 49 ++ examples/nrf54l15/src/bin/gpiote_port.rs | 33 + examples/nrf54l15/src/bin/pwm.rs | 86 +++ examples/nrf54l15/src/bin/saadc.rs | 28 + examples/nrf54l15/src/bin/spim.rs | 72 ++ examples/nrf54l15/src/bin/twim.rs | 37 + examples/nrf54l15/src/bin/twis.rs | 47 ++ examples/nrf54l15/src/bin/uart.rs | 37 + tests/nrf/.cargo/config.toml | 4 +- tests/nrf/src/bin/buffered_uart_spam.rs | 10 +- 50 files changed, 3774 insertions(+), 1648 deletions(-) delete mode 100644 embassy-nrf/src/buffered_uarte.rs create mode 100644 embassy-nrf/src/buffered_uarte/mod.rs create mode 100644 embassy-nrf/src/buffered_uarte/v1.rs create mode 100644 embassy-nrf/src/buffered_uarte/v2.rs create mode 100644 examples/nrf54l15/src/bin/buffered_uart.rs create mode 100644 examples/nrf54l15/src/bin/gpiote_channel.rs create mode 100644 examples/nrf54l15/src/bin/gpiote_port.rs create mode 100644 examples/nrf54l15/src/bin/pwm.rs create mode 100644 examples/nrf54l15/src/bin/saadc.rs create mode 100644 examples/nrf54l15/src/bin/spim.rs create mode 100644 examples/nrf54l15/src/bin/twim.rs create mode 100644 examples/nrf54l15/src/bin/twis.rs create mode 100644 examples/nrf54l15/src/bin/uart.rs diff --git a/embassy-net-nrf91/CHANGELOG.md b/embassy-net-nrf91/CHANGELOG.md index 52cbf5ef3..11974ac04 100644 --- a/embassy-net-nrf91/CHANGELOG.md +++ b/embassy-net-nrf91/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- changed: updated to nrf-pac with nrf52/nrf53/nrf91 register layout more similar to nrf54 + ## 0.1.1 - 2025-08-14 - First release with changelog. diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml index ecb10246a..75b7aeeb2 100644 --- a/embassy-net-nrf91/Cargo.toml +++ b/embassy-net-nrf91/Cargo.toml @@ -18,7 +18,7 @@ log = ["dep:log"] defmt = { version = "1.0.1", optional = true } log = { version = "0.4.14", optional = true } -nrf-pac = "0.1.0" +nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "58198c23bce72edc10b4e1656d1b54441fc74e7c" } cortex-m = "0.7.7" embassy-time = { version = "0.5.0", path = "../embassy-time" } diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index 89adaf2da..c23613f19 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - changed: allow configuring the PWM peripheral in the constructor of `SimplePwm` - changed: support setting duty cycles with inverted polarity in `SimplePwm` - added: support setting the duty cycles of all channels at once in `SimplePwm` +- changed: updated to nrf-pac with nrf52/nrf53/nrf91 register layout more similar to nrf54 +- added: support for nrf54l peripherals: uart, gpiote, twim, twis, spim, spis, dppi, pwm, saadc ## 0.8.0 - 2025-09-30 diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 28f137d5c..08f4b280b 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -104,17 +104,17 @@ qspi-multiwrite-flash = [] #! ### Chip selection features ## nRF51 -nrf51 = ["nrf-pac/nrf51", "_nrf51"] +nrf51 = ["nrf-pac/nrf51", "_nrf51", "_spi-v1"] ## nRF52805 -nrf52805 = ["nrf-pac/nrf52805", "_nrf52"] +nrf52805 = ["nrf-pac/nrf52805", "_nrf52", "_spi-v1"] ## nRF52810 -nrf52810 = ["nrf-pac/nrf52810", "_nrf52"] +nrf52810 = ["nrf-pac/nrf52810", "_nrf52", "_spi-v1"] ## nRF52811 -nrf52811 = ["nrf-pac/nrf52811", "_nrf52"] +nrf52811 = ["nrf-pac/nrf52811", "_nrf52", "_spi-v1"] ## nRF52820 -nrf52820 = ["nrf-pac/nrf52820", "_nrf52"] +nrf52820 = ["nrf-pac/nrf52820", "_nrf52", "_spi-v1"] ## nRF52832 -nrf52832 = ["nrf-pac/nrf52832", "_nrf52", "_nrf52832_anomaly_109"] +nrf52832 = ["nrf-pac/nrf52832", "_nrf52", "_nrf52832_anomaly_109", "_spi-v1"] ## nRF52833 nrf52833 = ["nrf-pac/nrf52833", "_nrf52", "_gpio-p1"] ## nRF52840 @@ -154,10 +154,10 @@ _nrf54l15-app = ["_nrf54l15", "nrf-pac/nrf54l15-app"] _nrf54l15 = ["_nrf54l", "_gpio-p1", "_gpio-p2"] _nrf54l = ["_dppi"] -_nrf9160 = ["nrf-pac/nrf9160", "_dppi"] -_nrf9120 = ["nrf-pac/nrf9120", "_dppi"] +_nrf9160 = ["nrf-pac/nrf9160", "_dppi", "_spi-v1"] +_nrf9120 = ["nrf-pac/nrf9120", "_dppi", "_spi-v1"] _nrf52 = ["_ppi"] -_nrf51 = ["_ppi"] +_nrf51 = ["_ppi", "_spi-v1"] _nrf91 = [] _time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"] @@ -172,6 +172,7 @@ _ppi = [] _dppi = [] _gpio-p1 = [] _gpio-p2 = [] +_spi-v1 = [] # Errata workarounds _nrf52832_anomaly_109 = [] @@ -199,7 +200,7 @@ embedded-io-async = { version = "0.6.1" } rand-core-06 = { package = "rand_core", version = "0.6" } rand-core-09 = { package = "rand_core", version = "0.9" } -nrf-pac = "0.1.0" +nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "58198c23bce72edc10b4e1656d1b54441fc74e7c" } defmt = { version = "1.0.1", optional = true } bitflags = "2.4.2" diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs deleted file mode 100644 index b1eb5c81a..000000000 --- a/embassy-nrf/src/buffered_uarte.rs +++ /dev/null @@ -1,951 +0,0 @@ -//! Async buffered UART driver. -//! -//! Note that discarding a future from a read or write operation may lead to losing -//! data. For example, when using `futures_util::future::select` and completion occurs -//! on the "other" future, you should capture the incomplete future and continue to use -//! it for the next read or write. This pattern is a consideration for all IO, and not -//! just serial communications. -//! -//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used. - -use core::cmp::min; -use core::future::{Future, poll_fn}; -use core::marker::PhantomData; -use core::slice; -use core::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering, compiler_fence}; -use core::task::Poll; - -use embassy_hal_internal::Peri; -use embassy_hal_internal::atomic_ring_buffer::RingBuffer; -use pac::uarte::vals; -// Re-export SVD variants to allow user to directly set values -pub use pac::uarte::vals::{Baudrate, ConfigParity as Parity}; - -use crate::gpio::{AnyPin, Pin as GpioPin}; -use crate::interrupt::InterruptExt; -use crate::interrupt::typelevel::Interrupt; -use crate::ppi::{ - self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task, -}; -use crate::timer::{Instance as TimerInstance, Timer}; -use crate::uarte::{Config, Instance as UarteInstance, configure, configure_rx_pins, configure_tx_pins, drop_tx_rx}; -use crate::{EASY_DMA_SIZE, interrupt, pac}; - -pub(crate) struct State { - tx_buf: RingBuffer, - tx_count: AtomicUsize, - - rx_buf: RingBuffer, - rx_started: AtomicBool, - rx_started_count: AtomicU8, - rx_ended_count: AtomicU8, - rx_ppi_ch: AtomicU8, - rx_overrun: AtomicBool, -} - -/// UART error. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] -pub enum Error { - /// Buffer Overrun - Overrun, -} - -impl State { - pub(crate) const fn new() -> Self { - Self { - tx_buf: RingBuffer::new(), - tx_count: AtomicUsize::new(0), - - rx_buf: RingBuffer::new(), - rx_started: AtomicBool::new(false), - rx_started_count: AtomicU8::new(0), - rx_ended_count: AtomicU8::new(0), - rx_ppi_ch: AtomicU8::new(0), - rx_overrun: AtomicBool::new(false), - } - } -} - -/// Interrupt handler. -pub struct InterruptHandler { - _phantom: PhantomData, -} - -impl interrupt::typelevel::Handler for InterruptHandler { - unsafe fn on_interrupt() { - //trace!("irq: start"); - let r = U::regs(); - let ss = U::state(); - let s = U::buffered_state(); - - if let Some(mut rx) = unsafe { s.rx_buf.try_writer() } { - let buf_len = s.rx_buf.len(); - let half_len = buf_len / 2; - - if r.events_error().read() != 0 { - r.events_error().write_value(0); - let errs = r.errorsrc().read(); - r.errorsrc().write_value(errs); - - if errs.overrun() { - s.rx_overrun.store(true, Ordering::Release); - ss.rx_waker.wake(); - } - } - - // Received some bytes, wake task. - if r.inten().read().rxdrdy() && r.events_rxdrdy().read() != 0 { - r.intenclr().write(|w| w.set_rxdrdy(true)); - r.events_rxdrdy().write_value(0); - ss.rx_waker.wake(); - } - - if r.events_endrx().read() != 0 { - //trace!(" irq_rx: endrx"); - r.events_endrx().write_value(0); - - let val = s.rx_ended_count.load(Ordering::Relaxed); - s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed); - } - - if r.events_rxstarted().read() != 0 || !s.rx_started.load(Ordering::Relaxed) { - //trace!(" irq_rx: rxstarted"); - let (ptr, len) = rx.push_buf(); - if len >= half_len { - r.events_rxstarted().write_value(0); - - //trace!(" irq_rx: starting second {:?}", half_len); - - // Set up the DMA read - r.rxd().ptr().write_value(ptr as u32); - r.rxd().maxcnt().write(|w| w.set_maxcnt(half_len as _)); - - let chn = s.rx_ppi_ch.load(Ordering::Relaxed); - - // Enable endrx -> startrx PPI channel. - // From this point on, if endrx happens, startrx is automatically fired. - ppi::regs().chenset().write(|w| w.0 = 1 << chn); - - // It is possible that endrx happened BEFORE enabling the PPI. In this case - // the PPI channel doesn't trigger, and we'd hang. We have to detect this - // and manually start. - - // check again in case endrx has happened between the last check and now. - if r.events_endrx().read() != 0 { - //trace!(" irq_rx: endrx"); - r.events_endrx().write_value(0); - - let val = s.rx_ended_count.load(Ordering::Relaxed); - s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed); - } - - let rx_ended = s.rx_ended_count.load(Ordering::Relaxed); - let rx_started = s.rx_started_count.load(Ordering::Relaxed); - - // If we started the same amount of transfers as ended, the last rxend has - // already occured. - let rxend_happened = rx_started == rx_ended; - - // Check if the PPI channel is still enabled. The PPI channel disables itself - // when it fires, so if it's still enabled it hasn't fired. - let ppi_ch_enabled = ppi::regs().chen().read().ch(chn as _); - - // if rxend happened, and the ppi channel hasn't fired yet, the rxend got missed. - // this condition also naturally matches if `!started`, needed to kickstart the DMA. - if rxend_happened && ppi_ch_enabled { - //trace!("manually starting."); - - // disable the ppi ch, it's of no use anymore. - ppi::regs().chenclr().write(|w| w.set_ch(chn as _, true)); - - // manually start - r.tasks_startrx().write_value(1); - } - - rx.push_done(half_len); - - s.rx_started_count.store(rx_started.wrapping_add(1), Ordering::Relaxed); - s.rx_started.store(true, Ordering::Relaxed); - } else { - //trace!(" irq_rx: rxstarted no buf"); - r.intenclr().write(|w| w.set_rxstarted(true)); - } - } - } - - // ============================= - - if let Some(mut tx) = unsafe { s.tx_buf.try_reader() } { - // TX end - if r.events_endtx().read() != 0 { - r.events_endtx().write_value(0); - - let n = s.tx_count.load(Ordering::Relaxed); - //trace!(" irq_tx: endtx {:?}", n); - tx.pop_done(n); - ss.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(); - let len = len.min(EASY_DMA_SIZE); - if len != 0 { - //trace!(" irq_tx: starting {:?}", len); - s.tx_count.store(len, Ordering::Relaxed); - - // Set up the DMA write - r.txd().ptr().write_value(ptr as u32); - r.txd().maxcnt().write(|w| w.set_maxcnt(len as _)); - - // Start UARTE Transmit transaction - r.tasks_starttx().write_value(1); - } - } - } - - //trace!("irq: end"); - } -} - -/// Buffered UARTE driver. -pub struct BufferedUarte<'d> { - tx: BufferedUarteTx<'d>, - rx: BufferedUarteRx<'d>, -} - -impl<'d> Unpin for BufferedUarte<'d> {} - -impl<'d> BufferedUarte<'d> { - /// Create a new BufferedUarte without hardware flow control. - /// - /// # Panics - /// - /// Panics if `rx_buffer.len()` is odd. - #[allow(clippy::too_many_arguments)] - pub fn new( - uarte: Peri<'d, U>, - timer: Peri<'d, T>, - ppi_ch1: Peri<'d, impl ConfigurableChannel>, - ppi_ch2: Peri<'d, impl ConfigurableChannel>, - ppi_group: Peri<'d, impl Group>, - rxd: Peri<'d, impl GpioPin>, - txd: Peri<'d, impl GpioPin>, - _irq: impl interrupt::typelevel::Binding> + 'd, - config: Config, - rx_buffer: &'d mut [u8], - tx_buffer: &'d mut [u8], - ) -> Self { - Self::new_inner( - uarte, - timer, - ppi_ch1.into(), - ppi_ch2.into(), - ppi_group.into(), - rxd.into(), - txd.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. - #[allow(clippy::too_many_arguments)] - pub fn new_with_rtscts( - uarte: Peri<'d, U>, - timer: Peri<'d, T>, - ppi_ch1: Peri<'d, impl ConfigurableChannel>, - ppi_ch2: Peri<'d, impl ConfigurableChannel>, - ppi_group: Peri<'d, impl Group>, - rxd: Peri<'d, impl GpioPin>, - txd: Peri<'d, impl GpioPin>, - cts: Peri<'d, impl GpioPin>, - rts: Peri<'d, impl GpioPin>, - _irq: impl interrupt::typelevel::Binding> + 'd, - config: Config, - rx_buffer: &'d mut [u8], - tx_buffer: &'d mut [u8], - ) -> Self { - Self::new_inner( - uarte, - timer, - ppi_ch1.into(), - ppi_ch2.into(), - ppi_group.into(), - rxd.into(), - txd.into(), - Some(cts.into()), - Some(rts.into()), - config, - rx_buffer, - tx_buffer, - ) - } - - #[allow(clippy::too_many_arguments)] - fn new_inner( - peri: Peri<'d, U>, - timer: Peri<'d, T>, - ppi_ch1: Peri<'d, AnyConfigurableChannel>, - ppi_ch2: Peri<'d, AnyConfigurableChannel>, - ppi_group: Peri<'d, AnyGroup>, - rxd: Peri<'d, AnyPin>, - txd: Peri<'d, AnyPin>, - cts: Option>, - rts: Option>, - config: Config, - rx_buffer: &'d mut [u8], - tx_buffer: &'d mut [u8], - ) -> Self { - let r = U::regs(); - let irq = U::Interrupt::IRQ; - let state = U::state(); - - configure(r, config, cts.is_some()); - - let tx = BufferedUarteTx::new_innerer(unsafe { peri.clone_unchecked() }, txd, cts, tx_buffer); - let rx = BufferedUarteRx::new_innerer(peri, timer, ppi_ch1, ppi_ch2, ppi_group, rxd, rts, rx_buffer); - - r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); - irq.pend(); - unsafe { irq.enable() }; - - state.tx_rx_refcount.store(2, Ordering::Relaxed); - - Self { tx, rx } - } - - /// Adjust the baud rate to the provided value. - pub fn set_baudrate(&mut self, baudrate: Baudrate) { - self.tx.set_baudrate(baudrate); - } - - /// Split the UART in reader and writer parts. - /// - /// This allows reading and writing concurrently from independent tasks. - pub fn split(self) -> (BufferedUarteRx<'d>, BufferedUarteTx<'d>) { - (self.rx, self.tx) - } - - /// Split the UART in reader and writer parts, by reference. - /// - /// The returned halves borrow from `self`, so you can drop them and go back to using - /// the "un-split" `self`. This allows temporarily splitting the UART. - pub fn split_by_ref(&mut self) -> (&mut BufferedUarteRx<'d>, &mut BufferedUarteTx<'d>) { - (&mut self.rx, &mut self.tx) - } - - /// 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 { - self.rx.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.rx.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.rx.consume(amt) - } - - /// Write a buffer into this writer, returning how many bytes were written. - pub async fn write(&mut self, buf: &[u8]) -> Result { - self.tx.write(buf).await - } - - /// Try writing a buffer without waiting, returning how many bytes were written. - pub fn try_write(&mut self, buf: &[u8]) -> Result { - self.tx.try_write(buf) - } - - /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. - pub async fn flush(&mut self) -> Result<(), Error> { - self.tx.flush().await - } -} - -/// Reader part of the buffered UARTE driver. -pub struct BufferedUarteTx<'d> { - r: pac::uarte::Uarte, - _irq: interrupt::Interrupt, - state: &'static crate::uarte::State, - buffered_state: &'static State, - _p: PhantomData<&'d ()>, -} - -impl<'d> BufferedUarteTx<'d> { - /// Create a new BufferedUarteTx without hardware flow control. - pub fn new( - uarte: Peri<'d, U>, - txd: Peri<'d, impl GpioPin>, - _irq: impl interrupt::typelevel::Binding> + 'd, - config: Config, - tx_buffer: &'d mut [u8], - ) -> Self { - Self::new_inner(uarte, txd.into(), None, config, tx_buffer) - } - - /// Create a new BufferedUarte with hardware flow control (RTS/CTS) - /// - /// # Panics - /// - /// Panics if `rx_buffer.len()` is odd. - pub fn new_with_cts( - uarte: Peri<'d, U>, - txd: Peri<'d, impl GpioPin>, - cts: Peri<'d, impl GpioPin>, - _irq: impl interrupt::typelevel::Binding> + 'd, - config: Config, - tx_buffer: &'d mut [u8], - ) -> Self { - Self::new_inner(uarte, txd.into(), Some(cts.into()), config, tx_buffer) - } - - fn new_inner( - peri: Peri<'d, U>, - txd: Peri<'d, AnyPin>, - cts: Option>, - config: Config, - tx_buffer: &'d mut [u8], - ) -> Self { - let r = U::regs(); - let irq = U::Interrupt::IRQ; - let state = U::state(); - let _buffered_state = U::buffered_state(); - - configure(r, config, cts.is_some()); - - let this = Self::new_innerer(peri, txd, cts, tx_buffer); - - r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); - irq.pend(); - unsafe { irq.enable() }; - - state.tx_rx_refcount.store(1, Ordering::Relaxed); - - this - } - - fn new_innerer( - _peri: Peri<'d, U>, - txd: Peri<'d, AnyPin>, - cts: Option>, - tx_buffer: &'d mut [u8], - ) -> Self { - let r = U::regs(); - let irq = U::Interrupt::IRQ; - let state = U::state(); - let buffered_state = U::buffered_state(); - - configure_tx_pins(r, txd, cts); - - // Initialize state - buffered_state.tx_count.store(0, Ordering::Relaxed); - let len = tx_buffer.len(); - unsafe { buffered_state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; - - r.events_txstarted().write_value(0); - - // Enable interrupts - r.intenset().write(|w| { - w.set_endtx(true); - }); - - Self { - r, - _irq: irq, - state, - buffered_state, - _p: PhantomData, - } - } - - /// Write a buffer into this writer, returning how many bytes were written. - pub fn write<'a>(&'a mut self, buf: &'a [u8]) -> impl Future> + 'a + use<'a, 'd> { - poll_fn(move |cx| { - //trace!("poll_write: {:?}", buf.len()); - let ss = self.state; - let s = self.buffered_state; - let mut tx = unsafe { s.tx_buf.writer() }; - - let tx_buf = tx.push_slice(); - if tx_buf.is_empty() { - //trace!("poll_write: pending"); - ss.tx_waker.register(cx.waker()); - return Poll::Pending; - } - - 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._irq.pend(); - - Poll::Ready(Ok(n)) - }) - } - - /// Try writing a buffer without waiting, returning how many bytes were written. - pub fn try_write(&mut self, buf: &[u8]) -> Result { - //trace!("poll_write: {:?}", buf.len()); - let s = self.buffered_state; - let mut tx = unsafe { s.tx_buf.writer() }; - - let tx_buf = tx.push_slice(); - if tx_buf.is_empty() { - return Ok(0); - } - - 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._irq.pend(); - - Ok(n) - } - - /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. - pub fn flush(&mut self) -> impl Future> + '_ { - let ss = self.state; - let s = self.buffered_state; - poll_fn(move |cx| { - //trace!("poll_flush"); - if !s.tx_buf.is_empty() { - //trace!("poll_flush: pending"); - ss.tx_waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - } - - /// Adjust the baud rate to the provided value. - pub fn set_baudrate(&mut self, baudrate: Baudrate) { - self.r.baudrate().write(|w| w.set_baudrate(baudrate)); - } -} - -impl<'a> Drop for BufferedUarteTx<'a> { - fn drop(&mut self) { - let r = self.r; - - r.intenclr().write(|w| { - w.set_txdrdy(true); - w.set_txstarted(true); - w.set_txstopped(true); - }); - r.events_txstopped().write_value(0); - r.tasks_stoptx().write_value(1); - while r.events_txstopped().read() == 0 {} - - let s = self.buffered_state; - unsafe { s.tx_buf.deinit() } - - let s = self.state; - drop_tx_rx(r, s); - } -} - -/// Reader part of the buffered UARTE driver. -pub struct BufferedUarteRx<'d> { - r: pac::uarte::Uarte, - state: &'static crate::uarte::State, - buffered_state: &'static State, - timer: Timer<'d>, - _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>, - _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>, - _ppi_group: PpiGroup<'d, AnyGroup>, - _p: PhantomData<&'d ()>, -} - -impl<'d> BufferedUarteRx<'d> { - /// Create a new BufferedUarte without hardware flow control. - /// - /// # Panics - /// - /// Panics if `rx_buffer.len()` is odd. - #[allow(clippy::too_many_arguments)] - pub fn new( - uarte: Peri<'d, U>, - timer: Peri<'d, T>, - ppi_ch1: Peri<'d, impl ConfigurableChannel>, - ppi_ch2: Peri<'d, impl ConfigurableChannel>, - ppi_group: Peri<'d, impl Group>, - _irq: impl interrupt::typelevel::Binding> + 'd, - rxd: Peri<'d, impl GpioPin>, - config: Config, - rx_buffer: &'d mut [u8], - ) -> Self { - Self::new_inner( - uarte, - timer, - ppi_ch1.into(), - ppi_ch2.into(), - ppi_group.into(), - rxd.into(), - None, - config, - rx_buffer, - ) - } - - /// Create a new BufferedUarte with hardware flow control (RTS/CTS) - /// - /// # Panics - /// - /// Panics if `rx_buffer.len()` is odd. - #[allow(clippy::too_many_arguments)] - pub fn new_with_rts( - uarte: Peri<'d, U>, - timer: Peri<'d, T>, - ppi_ch1: Peri<'d, impl ConfigurableChannel>, - ppi_ch2: Peri<'d, impl ConfigurableChannel>, - ppi_group: Peri<'d, impl Group>, - rxd: Peri<'d, impl GpioPin>, - rts: Peri<'d, impl GpioPin>, - _irq: impl interrupt::typelevel::Binding> + 'd, - config: Config, - rx_buffer: &'d mut [u8], - ) -> Self { - Self::new_inner( - uarte, - timer, - ppi_ch1.into(), - ppi_ch2.into(), - ppi_group.into(), - rxd.into(), - Some(rts.into()), - config, - rx_buffer, - ) - } - - #[allow(clippy::too_many_arguments)] - fn new_inner( - peri: Peri<'d, U>, - timer: Peri<'d, T>, - ppi_ch1: Peri<'d, AnyConfigurableChannel>, - ppi_ch2: Peri<'d, AnyConfigurableChannel>, - ppi_group: Peri<'d, AnyGroup>, - rxd: Peri<'d, AnyPin>, - rts: Option>, - config: Config, - rx_buffer: &'d mut [u8], - ) -> Self { - let r = U::regs(); - let irq = U::Interrupt::IRQ; - let state = U::state(); - let _buffered_state = U::buffered_state(); - - configure(r, config, rts.is_some()); - - let this = Self::new_innerer(peri, timer, ppi_ch1, ppi_ch2, ppi_group, rxd, rts, rx_buffer); - - r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); - irq.pend(); - unsafe { irq.enable() }; - - state.tx_rx_refcount.store(1, Ordering::Relaxed); - - this - } - - #[allow(clippy::too_many_arguments)] - fn new_innerer( - _peri: Peri<'d, U>, - timer: Peri<'d, T>, - ppi_ch1: Peri<'d, AnyConfigurableChannel>, - ppi_ch2: Peri<'d, AnyConfigurableChannel>, - ppi_group: Peri<'d, AnyGroup>, - rxd: Peri<'d, AnyPin>, - rts: Option>, - rx_buffer: &'d mut [u8], - ) -> Self { - assert!(rx_buffer.len() % 2 == 0); - - let r = U::regs(); - let state = U::state(); - let buffered_state = U::buffered_state(); - - configure_rx_pins(r, rxd, rts); - - // Initialize state - buffered_state.rx_started_count.store(0, Ordering::Relaxed); - buffered_state.rx_ended_count.store(0, Ordering::Relaxed); - buffered_state.rx_started.store(false, Ordering::Relaxed); - buffered_state.rx_overrun.store(false, Ordering::Relaxed); - let rx_len = rx_buffer.len().min(EASY_DMA_SIZE * 2); - unsafe { buffered_state.rx_buf.init(rx_buffer.as_mut_ptr(), rx_len) }; - - // clear errors - let errors = r.errorsrc().read(); - r.errorsrc().write_value(errors); - - r.events_rxstarted().write_value(0); - r.events_error().write_value(0); - r.events_endrx().write_value(0); - - // Enable interrupts - r.intenset().write(|w| { - w.set_endtx(true); - w.set_rxstarted(true); - w.set_error(true); - w.set_endrx(true); - }); - - // Configure byte counter. - let timer = Timer::new_counter(timer); - timer.cc(1).write(rx_len as u32 * 2); - timer.cc(1).short_compare_clear(); - timer.clear(); - timer.start(); - - let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(r.events_rxdrdy()), timer.task_count()); - ppi_ch1.enable(); - - buffered_state - .rx_ppi_ch - .store(ppi_ch2.number() as u8, Ordering::Relaxed); - let mut ppi_group = PpiGroup::new(ppi_group); - let mut ppi_ch2 = Ppi::new_one_to_two( - ppi_ch2, - Event::from_reg(r.events_endrx()), - Task::from_reg(r.tasks_startrx()), - ppi_group.task_disable_all(), - ); - ppi_ch2.disable(); - ppi_group.add_channel(&ppi_ch2); - - Self { - r, - state, - buffered_state, - timer, - _ppi_ch1: ppi_ch1, - _ppi_ch2: ppi_ch2, - _ppi_group: ppi_group, - _p: PhantomData, - } - } - - /// 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 { - let data = self.fill_buf().await?; - let n = data.len().min(buf.len()); - buf[..n].copy_from_slice(&data[..n]); - self.consume(n); - Ok(n) - } - - /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. - pub fn fill_buf(&mut self) -> impl Future> { - let r = self.r; - let s = self.buffered_state; - let ss = self.state; - let timer = &self.timer; - poll_fn(move |cx| { - compiler_fence(Ordering::SeqCst); - //trace!("poll_read"); - - if s.rx_overrun.swap(false, Ordering::Acquire) { - return Poll::Ready(Err(Error::Overrun)); - } - - // Read the RXDRDY counter. - timer.cc(0).capture(); - let mut end = timer.cc(0).read() as usize; - //trace!(" rxdrdy count = {:?}", end); - - // We've set a compare channel that resets the counter to 0 when it reaches `len*2`. - // However, it's unclear if that's instant, or there's a small window where you can - // still read `len()*2`. - // This could happen if in one clock cycle the counter is updated, and in the next the - // clear takes effect. The docs are very sparse, they just say "Task delays: After TIMER - // is started, the CLEAR, COUNT, and STOP tasks are guaranteed to take effect within one - // clock cycle of the PCLK16M." :shrug: - // So, we wrap the counter ourselves, just in case. - if end > s.rx_buf.len() * 2 { - end = 0 - } - - // This logic mirrors `atomic_ring_buffer::Reader::pop_buf()` - let mut start = s.rx_buf.start.load(Ordering::Relaxed); - let len = s.rx_buf.len(); - if start == end { - //trace!(" empty"); - ss.rx_waker.register(cx.waker()); - r.intenset().write(|w| w.set_rxdrdy(true)); - return Poll::Pending; - } - - if start >= len { - start -= len - } - if end >= len { - end -= len - } - - let n = if end > start { end - start } else { len - start }; - assert!(n != 0); - //trace!(" uarte ringbuf: pop_buf {:?}..{:?}", start, start + n); - - let buf = s.rx_buf.buf.load(Ordering::Relaxed); - Poll::Ready(Ok(unsafe { slice::from_raw_parts(buf.add(start), n) })) - }) - } - - /// 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) { - if amt == 0 { - return; - } - - let s = self.buffered_state; - let mut rx = unsafe { s.rx_buf.reader() }; - rx.pop_done(amt); - self.r.intenset().write(|w| w.set_rxstarted(true)); - } - - /// we are ready to read if there is data in the buffer - fn read_ready(&self) -> Result { - let state = self.buffered_state; - if state.rx_overrun.swap(false, Ordering::Acquire) { - return Err(Error::Overrun); - } - Ok(!state.rx_buf.is_empty()) - } -} - -impl<'a> Drop for BufferedUarteRx<'a> { - fn drop(&mut self) { - self._ppi_group.disable_all(); - - let r = self.r; - - self.timer.stop(); - - r.intenclr().write(|w| { - w.set_rxdrdy(true); - w.set_rxstarted(true); - w.set_rxto(true); - }); - r.events_rxto().write_value(0); - r.tasks_stoprx().write_value(1); - while r.events_rxto().read() == 0 {} - - let s = self.buffered_state; - unsafe { s.rx_buf.deinit() } - - let s = self.state; - drop_tx_rx(r, s); - } -} - -mod _embedded_io { - use super::*; - - impl embedded_io_async::Error for Error { - fn kind(&self) -> embedded_io_async::ErrorKind { - match *self { - Error::Overrun => embedded_io_async::ErrorKind::OutOfMemory, - } - } - } - - impl<'d> embedded_io_async::ErrorType for BufferedUarte<'d> { - type Error = Error; - } - - impl<'d> embedded_io_async::ErrorType for BufferedUarteRx<'d> { - type Error = Error; - } - - impl<'d> embedded_io_async::ErrorType for BufferedUarteTx<'d> { - type Error = Error; - } - - impl<'d> embedded_io_async::Read for BufferedUarte<'d> { - async fn read(&mut self, buf: &mut [u8]) -> Result { - self.read(buf).await - } - } - - impl<'d> embedded_io_async::Read for BufferedUarteRx<'d> { - async fn read(&mut self, buf: &mut [u8]) -> Result { - self.read(buf).await - } - } - - impl<'d> embedded_io_async::ReadReady for BufferedUarte<'d> { - fn read_ready(&mut self) -> Result { - self.rx.read_ready() - } - } - - impl<'d> embedded_io_async::ReadReady for BufferedUarteRx<'d> { - fn read_ready(&mut self) -> Result { - let state = self.buffered_state; - Ok(!state.rx_buf.is_empty()) - } - } - - impl<'d> embedded_io_async::BufRead for BufferedUarte<'d> { - async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - self.fill_buf().await - } - - fn consume(&mut self, amt: usize) { - self.consume(amt) - } - } - - impl<'d> embedded_io_async::BufRead for BufferedUarteRx<'d> { - async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - self.fill_buf().await - } - - fn consume(&mut self, amt: usize) { - self.consume(amt) - } - } - - impl<'d> embedded_io_async::Write for BufferedUarte<'d> { - async fn write(&mut self, buf: &[u8]) -> Result { - self.write(buf).await - } - - async fn flush(&mut self) -> Result<(), Self::Error> { - self.flush().await - } - } - - impl<'d> embedded_io_async::Write for BufferedUarteTx<'d> { - async fn write(&mut self, buf: &[u8]) -> Result { - self.write(buf).await - } - - async fn flush(&mut self) -> Result<(), Self::Error> { - self.flush().await - } - } -} diff --git a/embassy-nrf/src/buffered_uarte/mod.rs b/embassy-nrf/src/buffered_uarte/mod.rs new file mode 100644 index 000000000..75d84baac --- /dev/null +++ b/embassy-nrf/src/buffered_uarte/mod.rs @@ -0,0 +1,14 @@ +//! Async buffered UART driver. +//! +//! Note that discarding a future from a read or write operation may lead to losing +//! data. For example, when using `futures_util::future::select` and completion occurs +//! on the "other" future, you should capture the incomplete future and continue to use +//! it for the next read or write. This pattern is a consideration for all IO, and not +//! just serial communications. +//! +//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used. +#[cfg_attr(not(feature = "_nrf54l"), path = "v1.rs")] +#[cfg_attr(feature = "_nrf54l", path = "v2.rs")] +mod _version; + +pub use _version::*; diff --git a/embassy-nrf/src/buffered_uarte/v1.rs b/embassy-nrf/src/buffered_uarte/v1.rs new file mode 100644 index 000000000..07de22717 --- /dev/null +++ b/embassy-nrf/src/buffered_uarte/v1.rs @@ -0,0 +1,951 @@ +//! Async buffered UART driver. +//! +//! Note that discarding a future from a read or write operation may lead to losing +//! data. For example, when using `futures_util::future::select` and completion occurs +//! on the "other" future, you should capture the incomplete future and continue to use +//! it for the next read or write. This pattern is a consideration for all IO, and not +//! just serial communications. +//! +//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used. + +use core::cmp::min; +use core::future::{Future, poll_fn}; +use core::marker::PhantomData; +use core::slice; +use core::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering, compiler_fence}; +use core::task::Poll; + +use embassy_hal_internal::Peri; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; +use pac::uarte::vals; +// Re-export SVD variants to allow user to directly set values +pub use pac::uarte::vals::{Baudrate, ConfigParity as Parity}; + +use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::interrupt::InterruptExt; +use crate::interrupt::typelevel::Interrupt; +use crate::ppi::{ + self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task, +}; +use crate::timer::{Instance as TimerInstance, Timer}; +use crate::uarte::{Config, Instance as UarteInstance, configure, configure_rx_pins, configure_tx_pins, drop_tx_rx}; +use crate::{EASY_DMA_SIZE, interrupt, pac}; + +pub(crate) struct State { + tx_buf: RingBuffer, + tx_count: AtomicUsize, + + rx_buf: RingBuffer, + rx_started: AtomicBool, + rx_started_count: AtomicU8, + rx_ended_count: AtomicU8, + rx_ppi_ch: AtomicU8, + rx_overrun: AtomicBool, +} + +/// UART error. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// Buffer Overrun + Overrun, +} + +impl State { + pub(crate) const fn new() -> Self { + Self { + tx_buf: RingBuffer::new(), + tx_count: AtomicUsize::new(0), + + rx_buf: RingBuffer::new(), + rx_started: AtomicBool::new(false), + rx_started_count: AtomicU8::new(0), + rx_ended_count: AtomicU8::new(0), + rx_ppi_ch: AtomicU8::new(0), + rx_overrun: AtomicBool::new(false), + } + } +} + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + //trace!("irq: start"); + let r = U::regs(); + let ss = U::state(); + let s = U::buffered_state(); + + if let Some(mut rx) = unsafe { s.rx_buf.try_writer() } { + let buf_len = s.rx_buf.len(); + let half_len = buf_len / 2; + + if r.events_error().read() != 0 { + r.events_error().write_value(0); + let errs = r.errorsrc().read(); + r.errorsrc().write_value(errs); + + if errs.overrun() { + s.rx_overrun.store(true, Ordering::Release); + ss.rx_waker.wake(); + } + } + + // Received some bytes, wake task. + if r.inten().read().rxdrdy() && r.events_rxdrdy().read() != 0 { + r.intenclr().write(|w| w.set_rxdrdy(true)); + r.events_rxdrdy().write_value(0); + ss.rx_waker.wake(); + } + + if r.events_dma().rx().end().read() != 0 { + //trace!(" irq_rx: endrx"); + r.events_dma().rx().end().write_value(0); + + let val = s.rx_ended_count.load(Ordering::Relaxed); + s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed); + } + + if r.events_dma().rx().ready().read() != 0 || !s.rx_started.load(Ordering::Relaxed) { + //trace!(" irq_rx: rxstarted"); + let (ptr, len) = rx.push_buf(); + if len >= half_len { + r.events_dma().rx().ready().write_value(0); + + //trace!(" irq_rx: starting second {:?}", half_len); + + // Set up the DMA read + r.dma().rx().ptr().write_value(ptr as u32); + r.dma().rx().maxcnt().write(|w| w.set_maxcnt(half_len as _)); + + let chn = s.rx_ppi_ch.load(Ordering::Relaxed); + + // Enable endrx -> startrx PPI channel. + // From this point on, if endrx happens, startrx is automatically fired. + ppi::regs().chenset().write(|w| w.0 = 1 << chn); + + // It is possible that endrx happened BEFORE enabling the PPI. In this case + // the PPI channel doesn't trigger, and we'd hang. We have to detect this + // and manually start. + + // check again in case endrx has happened between the last check and now. + if r.events_dma().rx().end().read() != 0 { + //trace!(" irq_rx: endrx"); + r.events_dma().rx().end().write_value(0); + + let val = s.rx_ended_count.load(Ordering::Relaxed); + s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed); + } + + let rx_ended = s.rx_ended_count.load(Ordering::Relaxed); + let rx_started = s.rx_started_count.load(Ordering::Relaxed); + + // If we started the same amount of transfers as ended, the last rxend has + // already occured. + let rxend_happened = rx_started == rx_ended; + + // Check if the PPI channel is still enabled. The PPI channel disables itself + // when it fires, so if it's still enabled it hasn't fired. + let ppi_ch_enabled = ppi::regs().chen().read().ch(chn as _); + + // if rxend happened, and the ppi channel hasn't fired yet, the rxend got missed. + // this condition also naturally matches if `!started`, needed to kickstart the DMA. + if rxend_happened && ppi_ch_enabled { + //trace!("manually starting."); + + // disable the ppi ch, it's of no use anymore. + ppi::regs().chenclr().write(|w| w.set_ch(chn as _, true)); + + // manually start + r.tasks_dma().rx().start().write_value(1); + } + + rx.push_done(half_len); + + s.rx_started_count.store(rx_started.wrapping_add(1), Ordering::Relaxed); + s.rx_started.store(true, Ordering::Relaxed); + } else { + //trace!(" irq_rx: rxstarted no buf"); + r.intenclr().write(|w| w.set_dmarxready(true)); + } + } + } + + // ============================= + + if let Some(mut tx) = unsafe { s.tx_buf.try_reader() } { + // TX end + if r.events_dma().tx().end().read() != 0 { + r.events_dma().tx().end().write_value(0); + + let n = s.tx_count.load(Ordering::Relaxed); + //trace!(" irq_tx: endtx {:?}", n); + tx.pop_done(n); + ss.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(); + let len = len.min(EASY_DMA_SIZE); + if len != 0 { + //trace!(" irq_tx: starting {:?}", len); + s.tx_count.store(len, Ordering::Relaxed); + + // Set up the DMA write + r.dma().tx().ptr().write_value(ptr as u32); + r.dma().tx().maxcnt().write(|w| w.set_maxcnt(len as _)); + + // Start UARTE Transmit transaction + r.tasks_dma().tx().start().write_value(1); + } + } + } + + //trace!("irq: end"); + } +} + +/// Buffered UARTE driver. +pub struct BufferedUarte<'d> { + tx: BufferedUarteTx<'d>, + rx: BufferedUarteRx<'d>, +} + +impl<'d> Unpin for BufferedUarte<'d> {} + +impl<'d> BufferedUarte<'d> { + /// Create a new BufferedUarte without hardware flow control. + /// + /// # Panics + /// + /// Panics if `rx_buffer.len()` is odd. + #[allow(clippy::too_many_arguments)] + pub fn new( + uarte: Peri<'d, U>, + timer: Peri<'d, T>, + ppi_ch1: Peri<'d, impl ConfigurableChannel>, + ppi_ch2: Peri<'d, impl ConfigurableChannel>, + ppi_group: Peri<'d, impl Group>, + rxd: Peri<'d, impl GpioPin>, + txd: Peri<'d, impl GpioPin>, + _irq: impl interrupt::typelevel::Binding> + 'd, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + Self::new_inner( + uarte, + timer, + ppi_ch1.into(), + ppi_ch2.into(), + ppi_group.into(), + rxd.into(), + txd.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. + #[allow(clippy::too_many_arguments)] + pub fn new_with_rtscts( + uarte: Peri<'d, U>, + timer: Peri<'d, T>, + ppi_ch1: Peri<'d, impl ConfigurableChannel>, + ppi_ch2: Peri<'d, impl ConfigurableChannel>, + ppi_group: Peri<'d, impl Group>, + rxd: Peri<'d, impl GpioPin>, + txd: Peri<'d, impl GpioPin>, + cts: Peri<'d, impl GpioPin>, + rts: Peri<'d, impl GpioPin>, + _irq: impl interrupt::typelevel::Binding> + 'd, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + Self::new_inner( + uarte, + timer, + ppi_ch1.into(), + ppi_ch2.into(), + ppi_group.into(), + rxd.into(), + txd.into(), + Some(cts.into()), + Some(rts.into()), + config, + rx_buffer, + tx_buffer, + ) + } + + #[allow(clippy::too_many_arguments)] + fn new_inner( + peri: Peri<'d, U>, + timer: Peri<'d, T>, + ppi_ch1: Peri<'d, AnyConfigurableChannel>, + ppi_ch2: Peri<'d, AnyConfigurableChannel>, + ppi_group: Peri<'d, AnyGroup>, + rxd: Peri<'d, AnyPin>, + txd: Peri<'d, AnyPin>, + cts: Option>, + rts: Option>, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + let r = U::regs(); + let irq = U::Interrupt::IRQ; + let state = U::state(); + + configure(r, config, cts.is_some()); + + let tx = BufferedUarteTx::new_innerer(unsafe { peri.clone_unchecked() }, txd, cts, tx_buffer); + let rx = BufferedUarteRx::new_innerer(peri, timer, ppi_ch1, ppi_ch2, ppi_group, rxd, rts, rx_buffer); + + r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); + irq.pend(); + unsafe { irq.enable() }; + + state.tx_rx_refcount.store(2, Ordering::Relaxed); + + Self { tx, rx } + } + + /// Adjust the baud rate to the provided value. + pub fn set_baudrate(&mut self, baudrate: Baudrate) { + self.tx.set_baudrate(baudrate); + } + + /// Split the UART in reader and writer parts. + /// + /// This allows reading and writing concurrently from independent tasks. + pub fn split(self) -> (BufferedUarteRx<'d>, BufferedUarteTx<'d>) { + (self.rx, self.tx) + } + + /// Split the UART in reader and writer parts, by reference. + /// + /// The returned halves borrow from `self`, so you can drop them and go back to using + /// the "un-split" `self`. This allows temporarily splitting the UART. + pub fn split_by_ref(&mut self) -> (&mut BufferedUarteRx<'d>, &mut BufferedUarteTx<'d>) { + (&mut self.rx, &mut self.tx) + } + + /// 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 { + self.rx.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.rx.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.rx.consume(amt) + } + + /// Write a buffer into this writer, returning how many bytes were written. + pub async fn write(&mut self, buf: &[u8]) -> Result { + self.tx.write(buf).await + } + + /// Try writing a buffer without waiting, returning how many bytes were written. + pub fn try_write(&mut self, buf: &[u8]) -> Result { + self.tx.try_write(buf) + } + + /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + pub async fn flush(&mut self) -> Result<(), Error> { + self.tx.flush().await + } +} + +/// Reader part of the buffered UARTE driver. +pub struct BufferedUarteTx<'d> { + r: pac::uarte::Uarte, + _irq: interrupt::Interrupt, + state: &'static crate::uarte::State, + buffered_state: &'static State, + _p: PhantomData<&'d ()>, +} + +impl<'d> BufferedUarteTx<'d> { + /// Create a new BufferedUarteTx without hardware flow control. + pub fn new( + uarte: Peri<'d, U>, + txd: Peri<'d, impl GpioPin>, + _irq: impl interrupt::typelevel::Binding> + 'd, + config: Config, + tx_buffer: &'d mut [u8], + ) -> Self { + Self::new_inner(uarte, txd.into(), None, config, tx_buffer) + } + + /// Create a new BufferedUarte with hardware flow control (RTS/CTS) + /// + /// # Panics + /// + /// Panics if `rx_buffer.len()` is odd. + pub fn new_with_cts( + uarte: Peri<'d, U>, + txd: Peri<'d, impl GpioPin>, + cts: Peri<'d, impl GpioPin>, + _irq: impl interrupt::typelevel::Binding> + 'd, + config: Config, + tx_buffer: &'d mut [u8], + ) -> Self { + Self::new_inner(uarte, txd.into(), Some(cts.into()), config, tx_buffer) + } + + fn new_inner( + peri: Peri<'d, U>, + txd: Peri<'d, AnyPin>, + cts: Option>, + config: Config, + tx_buffer: &'d mut [u8], + ) -> Self { + let r = U::regs(); + let irq = U::Interrupt::IRQ; + let state = U::state(); + let _buffered_state = U::buffered_state(); + + configure(r, config, cts.is_some()); + + let this = Self::new_innerer(peri, txd, cts, tx_buffer); + + r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); + irq.pend(); + unsafe { irq.enable() }; + + state.tx_rx_refcount.store(1, Ordering::Relaxed); + + this + } + + fn new_innerer( + _peri: Peri<'d, U>, + txd: Peri<'d, AnyPin>, + cts: Option>, + tx_buffer: &'d mut [u8], + ) -> Self { + let r = U::regs(); + let irq = U::Interrupt::IRQ; + let state = U::state(); + let buffered_state = U::buffered_state(); + + configure_tx_pins(r, txd, cts); + + // Initialize state + buffered_state.tx_count.store(0, Ordering::Relaxed); + let len = tx_buffer.len(); + unsafe { buffered_state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + + r.events_dma().tx().ready().write_value(0); + + // Enable interrupts + r.intenset().write(|w| { + w.set_dmatxend(true); + }); + + Self { + r, + _irq: irq, + state, + buffered_state, + _p: PhantomData, + } + } + + /// Write a buffer into this writer, returning how many bytes were written. + pub fn write<'a>(&'a mut self, buf: &'a [u8]) -> impl Future> + 'a + use<'a, 'd> { + poll_fn(move |cx| { + //trace!("poll_write: {:?}", buf.len()); + let ss = self.state; + let s = self.buffered_state; + let mut tx = unsafe { s.tx_buf.writer() }; + + let tx_buf = tx.push_slice(); + if tx_buf.is_empty() { + //trace!("poll_write: pending"); + ss.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + 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._irq.pend(); + + Poll::Ready(Ok(n)) + }) + } + + /// Try writing a buffer without waiting, returning how many bytes were written. + pub fn try_write(&mut self, buf: &[u8]) -> Result { + //trace!("poll_write: {:?}", buf.len()); + let s = self.buffered_state; + let mut tx = unsafe { s.tx_buf.writer() }; + + let tx_buf = tx.push_slice(); + if tx_buf.is_empty() { + return Ok(0); + } + + 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._irq.pend(); + + Ok(n) + } + + /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + pub fn flush(&mut self) -> impl Future> + '_ { + let ss = self.state; + let s = self.buffered_state; + poll_fn(move |cx| { + //trace!("poll_flush"); + if !s.tx_buf.is_empty() { + //trace!("poll_flush: pending"); + ss.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + } + + /// Adjust the baud rate to the provided value. + pub fn set_baudrate(&mut self, baudrate: Baudrate) { + self.r.baudrate().write(|w| w.set_baudrate(baudrate)); + } +} + +impl<'a> Drop for BufferedUarteTx<'a> { + fn drop(&mut self) { + let r = self.r; + + r.intenclr().write(|w| { + w.set_txdrdy(true); + w.set_dmatxready(true); + w.set_txstopped(true); + }); + r.events_txstopped().write_value(0); + r.tasks_dma().tx().stop().write_value(1); + while r.events_txstopped().read() == 0 {} + + let s = self.buffered_state; + unsafe { s.tx_buf.deinit() } + + let s = self.state; + drop_tx_rx(r, s); + } +} + +/// Reader part of the buffered UARTE driver. +pub struct BufferedUarteRx<'d> { + r: pac::uarte::Uarte, + state: &'static crate::uarte::State, + buffered_state: &'static State, + timer: Timer<'d>, + _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>, + _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>, + _ppi_group: PpiGroup<'d, AnyGroup>, + _p: PhantomData<&'d ()>, +} + +impl<'d> BufferedUarteRx<'d> { + /// Create a new BufferedUarte without hardware flow control. + /// + /// # Panics + /// + /// Panics if `rx_buffer.len()` is odd. + #[allow(clippy::too_many_arguments)] + pub fn new( + uarte: Peri<'d, U>, + timer: Peri<'d, T>, + ppi_ch1: Peri<'d, impl ConfigurableChannel>, + ppi_ch2: Peri<'d, impl ConfigurableChannel>, + ppi_group: Peri<'d, impl Group>, + _irq: impl interrupt::typelevel::Binding> + 'd, + rxd: Peri<'d, impl GpioPin>, + config: Config, + rx_buffer: &'d mut [u8], + ) -> Self { + Self::new_inner( + uarte, + timer, + ppi_ch1.into(), + ppi_ch2.into(), + ppi_group.into(), + rxd.into(), + None, + config, + rx_buffer, + ) + } + + /// Create a new BufferedUarte with hardware flow control (RTS/CTS) + /// + /// # Panics + /// + /// Panics if `rx_buffer.len()` is odd. + #[allow(clippy::too_many_arguments)] + pub fn new_with_rts( + uarte: Peri<'d, U>, + timer: Peri<'d, T>, + ppi_ch1: Peri<'d, impl ConfigurableChannel>, + ppi_ch2: Peri<'d, impl ConfigurableChannel>, + ppi_group: Peri<'d, impl Group>, + rxd: Peri<'d, impl GpioPin>, + rts: Peri<'d, impl GpioPin>, + _irq: impl interrupt::typelevel::Binding> + 'd, + config: Config, + rx_buffer: &'d mut [u8], + ) -> Self { + Self::new_inner( + uarte, + timer, + ppi_ch1.into(), + ppi_ch2.into(), + ppi_group.into(), + rxd.into(), + Some(rts.into()), + config, + rx_buffer, + ) + } + + #[allow(clippy::too_many_arguments)] + fn new_inner( + peri: Peri<'d, U>, + timer: Peri<'d, T>, + ppi_ch1: Peri<'d, AnyConfigurableChannel>, + ppi_ch2: Peri<'d, AnyConfigurableChannel>, + ppi_group: Peri<'d, AnyGroup>, + rxd: Peri<'d, AnyPin>, + rts: Option>, + config: Config, + rx_buffer: &'d mut [u8], + ) -> Self { + let r = U::regs(); + let irq = U::Interrupt::IRQ; + let state = U::state(); + let _buffered_state = U::buffered_state(); + + configure(r, config, rts.is_some()); + + let this = Self::new_innerer(peri, timer, ppi_ch1, ppi_ch2, ppi_group, rxd, rts, rx_buffer); + + r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); + irq.pend(); + unsafe { irq.enable() }; + + state.tx_rx_refcount.store(1, Ordering::Relaxed); + + this + } + + #[allow(clippy::too_many_arguments)] + fn new_innerer( + _peri: Peri<'d, U>, + timer: Peri<'d, T>, + ppi_ch1: Peri<'d, AnyConfigurableChannel>, + ppi_ch2: Peri<'d, AnyConfigurableChannel>, + ppi_group: Peri<'d, AnyGroup>, + rxd: Peri<'d, AnyPin>, + rts: Option>, + rx_buffer: &'d mut [u8], + ) -> Self { + assert!(rx_buffer.len() % 2 == 0); + + let r = U::regs(); + let state = U::state(); + let buffered_state = U::buffered_state(); + + configure_rx_pins(r, rxd, rts); + + // Initialize state + buffered_state.rx_started_count.store(0, Ordering::Relaxed); + buffered_state.rx_ended_count.store(0, Ordering::Relaxed); + buffered_state.rx_started.store(false, Ordering::Relaxed); + buffered_state.rx_overrun.store(false, Ordering::Relaxed); + let rx_len = rx_buffer.len().min(EASY_DMA_SIZE * 2); + unsafe { buffered_state.rx_buf.init(rx_buffer.as_mut_ptr(), rx_len) }; + + // clear errors + let errors = r.errorsrc().read(); + r.errorsrc().write_value(errors); + + r.events_dma().rx().ready().write_value(0); + r.events_error().write_value(0); + r.events_dma().rx().end().write_value(0); + + // Enable interrupts + r.intenset().write(|w| { + w.set_dmatxend(true); + w.set_dmarxready(true); + w.set_error(true); + w.set_dmarxend(true); + }); + + // Configure byte counter. + let timer = Timer::new_counter(timer); + timer.cc(1).write(rx_len as u32 * 2); + timer.cc(1).short_compare_clear(); + timer.clear(); + timer.start(); + + let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(r.events_rxdrdy()), timer.task_count()); + ppi_ch1.enable(); + + buffered_state + .rx_ppi_ch + .store(ppi_ch2.number() as u8, Ordering::Relaxed); + let mut ppi_group = PpiGroup::new(ppi_group); + let mut ppi_ch2 = Ppi::new_one_to_two( + ppi_ch2, + Event::from_reg(r.events_dma().rx().end()), + Task::from_reg(r.tasks_dma().rx().start()), + ppi_group.task_disable_all(), + ); + ppi_ch2.disable(); + ppi_group.add_channel(&ppi_ch2); + + Self { + r, + state, + buffered_state, + timer, + _ppi_ch1: ppi_ch1, + _ppi_ch2: ppi_ch2, + _ppi_group: ppi_group, + _p: PhantomData, + } + } + + /// 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 { + let data = self.fill_buf().await?; + let n = data.len().min(buf.len()); + buf[..n].copy_from_slice(&data[..n]); + self.consume(n); + Ok(n) + } + + /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + pub fn fill_buf(&mut self) -> impl Future> { + let r = self.r; + let s = self.buffered_state; + let ss = self.state; + let timer = &self.timer; + poll_fn(move |cx| { + compiler_fence(Ordering::SeqCst); + //trace!("poll_read"); + + if s.rx_overrun.swap(false, Ordering::Acquire) { + return Poll::Ready(Err(Error::Overrun)); + } + + // Read the RXDRDY counter. + timer.cc(0).capture(); + let mut end = timer.cc(0).read() as usize; + //trace!(" rxdrdy count = {:?}", end); + + // We've set a compare channel that resets the counter to 0 when it reaches `len*2`. + // However, it's unclear if that's instant, or there's a small window where you can + // still read `len()*2`. + // This could happen if in one clock cycle the counter is updated, and in the next the + // clear takes effect. The docs are very sparse, they just say "Task delays: After TIMER + // is started, the CLEAR, COUNT, and STOP tasks are guaranteed to take effect within one + // clock cycle of the PCLK16M." :shrug: + // So, we wrap the counter ourselves, just in case. + if end > s.rx_buf.len() * 2 { + end = 0 + } + + // This logic mirrors `atomic_ring_buffer::Reader::pop_buf()` + let mut start = s.rx_buf.start.load(Ordering::Relaxed); + let len = s.rx_buf.len(); + if start == end { + //trace!(" empty"); + ss.rx_waker.register(cx.waker()); + r.intenset().write(|w| w.set_rxdrdy(true)); + return Poll::Pending; + } + + if start >= len { + start -= len + } + if end >= len { + end -= len + } + + let n = if end > start { end - start } else { len - start }; + assert!(n != 0); + //trace!(" uarte ringbuf: pop_buf {:?}..{:?}", start, start + n); + + let buf = s.rx_buf.buf.load(Ordering::Relaxed); + Poll::Ready(Ok(unsafe { slice::from_raw_parts(buf.add(start), n) })) + }) + } + + /// 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) { + if amt == 0 { + return; + } + + let s = self.buffered_state; + let mut rx = unsafe { s.rx_buf.reader() }; + rx.pop_done(amt); + self.r.intenset().write(|w| w.set_dmarxready(true)); + } + + /// we are ready to read if there is data in the buffer + fn read_ready(&self) -> Result { + let state = self.buffered_state; + if state.rx_overrun.swap(false, Ordering::Acquire) { + return Err(Error::Overrun); + } + Ok(!state.rx_buf.is_empty()) + } +} + +impl<'a> Drop for BufferedUarteRx<'a> { + fn drop(&mut self) { + self._ppi_group.disable_all(); + + let r = self.r; + + self.timer.stop(); + + r.intenclr().write(|w| { + w.set_rxdrdy(true); + w.set_dmarxready(true); + w.set_rxto(true); + }); + r.events_rxto().write_value(0); + r.tasks_dma().rx().stop().write_value(1); + while r.events_rxto().read() == 0 {} + + let s = self.buffered_state; + unsafe { s.rx_buf.deinit() } + + let s = self.state; + drop_tx_rx(r, s); + } +} + +mod _embedded_io { + use super::*; + + impl embedded_io_async::Error for Error { + fn kind(&self) -> embedded_io_async::ErrorKind { + match *self { + Error::Overrun => embedded_io_async::ErrorKind::OutOfMemory, + } + } + } + + impl<'d> embedded_io_async::ErrorType for BufferedUarte<'d> { + type Error = Error; + } + + impl<'d> embedded_io_async::ErrorType for BufferedUarteRx<'d> { + type Error = Error; + } + + impl<'d> embedded_io_async::ErrorType for BufferedUarteTx<'d> { + type Error = Error; + } + + impl<'d> embedded_io_async::Read for BufferedUarte<'d> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read(buf).await + } + } + + impl<'d> embedded_io_async::Read for BufferedUarteRx<'d> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read(buf).await + } + } + + impl<'d> embedded_io_async::ReadReady for BufferedUarte<'d> { + fn read_ready(&mut self) -> Result { + self.rx.read_ready() + } + } + + impl<'d> embedded_io_async::ReadReady for BufferedUarteRx<'d> { + fn read_ready(&mut self) -> Result { + let state = self.buffered_state; + Ok(!state.rx_buf.is_empty()) + } + } + + impl<'d> embedded_io_async::BufRead for BufferedUarte<'d> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.fill_buf().await + } + + fn consume(&mut self, amt: usize) { + self.consume(amt) + } + } + + impl<'d> embedded_io_async::BufRead for BufferedUarteRx<'d> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.fill_buf().await + } + + fn consume(&mut self, amt: usize) { + self.consume(amt) + } + } + + impl<'d> embedded_io_async::Write for BufferedUarte<'d> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write(buf).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.flush().await + } + } + + impl<'d> embedded_io_async::Write for BufferedUarteTx<'d> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write(buf).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.flush().await + } + } +} diff --git a/embassy-nrf/src/buffered_uarte/v2.rs b/embassy-nrf/src/buffered_uarte/v2.rs new file mode 100644 index 000000000..d0d2d97d1 --- /dev/null +++ b/embassy-nrf/src/buffered_uarte/v2.rs @@ -0,0 +1,687 @@ +//! Async buffered UART driver. +//! +//! Note that discarding a future from a read or write operation may lead to losing +//! data. For example, when using `futures_util::future::select` and completion occurs +//! on the "other" future, you should capture the incomplete future and continue to use +//! it for the next read or write. This pattern is a consideration for all IO, and not +//! just serial communications. +//! +//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used. +//! +//! The code is based on the generic buffered_uarte implementation but uses the nrf54l +//! frame timeout event to correctly determine the size of transferred data. +//! Counting of rxrdy events, used in the generic implementation, cannot be applied +//! to nrf54l chips, as they buffer up to 4 bytes in a single DMA transaction. +//! The only reliable way to find the number of bytes received is to stop the transfer, +//! wait for the DMA stopped event, and read the value in the rx.dma.amount register. +//! This also flushes all in-flight data to RAM. + +use core::cmp::min; +use core::future::{Future, poll_fn}; +use core::marker::PhantomData; +use core::slice; +use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering, compiler_fence}; +use core::task::Poll; + +use embassy_hal_internal::Peri; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; +use pac::uarte::vals; +// Re-export SVD variants to allow user to directly set values +pub use pac::uarte::vals::{Baudrate, ConfigParity as Parity}; + +use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::interrupt::typelevel::Interrupt; +use crate::uarte::{Config, Instance as UarteInstance, configure, configure_rx_pins, configure_tx_pins, drop_tx_rx}; +use crate::{EASY_DMA_SIZE, interrupt, pac}; + +pub(crate) struct State { + tx_buf: RingBuffer, + tx_count: AtomicUsize, + + rx_buf: RingBuffer, + rx_started: AtomicBool, +} + +/// UART error. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + // No errors for now +} + +impl State { + pub(crate) const fn new() -> Self { + Self { + tx_buf: RingBuffer::new(), + tx_count: AtomicUsize::new(0), + + rx_buf: RingBuffer::new(), + rx_started: AtomicBool::new(false), + } + } +} + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + info!("irq: start"); + let r = U::regs(); + let ss = U::state(); + let s = U::buffered_state(); + + if let Some(mut rx) = unsafe { s.rx_buf.try_writer() } { + let buf_len = s.rx_buf.len(); + let half_len = buf_len / 2; + + if r.events_error().read() != 0 { + r.events_error().write_value(0); + let errs = r.errorsrc().read(); + r.errorsrc().write_value(errs); + + if errs.overrun() { + panic!("BufferedUarte UART overrun"); + } + } + + let first_run = !s.rx_started.swap(true, Ordering::Relaxed); + if r.events_dma().rx().end().read() != 0 || first_run { + //trace!(" irq_rx: endrx"); + r.events_dma().rx().end().write_value(0); + + if !first_run { + // Received some bytes, wake task. + let rxed = r.dma().rx().amount().read().amount() as usize; + rx.push_done(rxed); + ss.rx_waker.wake(); + } + + let (ptr, len) = rx.push_buf(); + if len == 0 { + panic!("BufferedUarte buffer overrun"); + } + + let len = if len > half_len { half_len } else { len }; + + // Set up the DMA read + r.dma().rx().ptr().write_value(ptr as u32); + r.dma().rx().maxcnt().write(|w| w.set_maxcnt(len as _)); + + // manually start + r.tasks_dma().rx().start().write_value(1); + } + } + + // ============================= + + if let Some(mut tx) = unsafe { s.tx_buf.try_reader() } { + // TX end + if r.events_dma().tx().end().read() != 0 { + r.events_dma().tx().end().write_value(0); + + let n = s.tx_count.load(Ordering::Relaxed); + //trace!(" irq_tx: endtx {:?}", n); + tx.pop_done(n); + ss.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(); + let len = len.min(EASY_DMA_SIZE); + if len != 0 { + //trace!(" irq_tx: starting {:?}", len); + s.tx_count.store(len, Ordering::Relaxed); + + // Set up the DMA write + r.dma().tx().ptr().write_value(ptr as u32); + r.dma().tx().maxcnt().write(|w| w.set_maxcnt(len as _)); + + // Start UARTE Transmit transaction + r.tasks_dma().tx().start().write_value(1); + } + } + } + + //trace!("irq: end"); + } +} + +/// Buffered UARTE driver. +pub struct BufferedUarte<'d, U: UarteInstance> { + tx: BufferedUarteTx<'d, U>, + rx: BufferedUarteRx<'d, U>, +} + +impl<'d, U: UarteInstance> Unpin for BufferedUarte<'d, U> {} + +impl<'d, U: UarteInstance> BufferedUarte<'d, U> { + /// Create a new BufferedUarte without hardware flow control. + #[allow(clippy::too_many_arguments)] + pub fn new( + uarte: Peri<'d, U>, + rxd: Peri<'d, impl GpioPin>, + txd: Peri<'d, impl GpioPin>, + _irq: impl interrupt::typelevel::Binding> + 'd, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + Self::new_inner(uarte, rxd.into(), txd.into(), None, None, config, rx_buffer, tx_buffer) + } + + /// Create a new BufferedUarte with hardware flow control (RTS/CTS) + #[allow(clippy::too_many_arguments)] + pub fn new_with_rtscts( + uarte: Peri<'d, U>, + rxd: Peri<'d, impl GpioPin>, + txd: Peri<'d, impl GpioPin>, + cts: Peri<'d, impl GpioPin>, + rts: Peri<'d, impl GpioPin>, + _irq: impl interrupt::typelevel::Binding> + 'd, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + Self::new_inner( + uarte, + rxd.into(), + txd.into(), + Some(cts.into()), + Some(rts.into()), + config, + rx_buffer, + tx_buffer, + ) + } + + #[allow(clippy::too_many_arguments)] + fn new_inner( + peri: Peri<'d, U>, + rxd: Peri<'d, AnyPin>, + txd: Peri<'d, AnyPin>, + cts: Option>, + rts: Option>, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + configure(U::regs(), config, cts.is_some()); + + let tx = BufferedUarteTx::new_innerer(unsafe { peri.clone_unchecked() }, txd, cts, tx_buffer); + let rx = BufferedUarteRx::new_innerer(peri, rxd, rts, rx_buffer); + + U::regs().enable().write(|w| w.set_enable(vals::Enable::ENABLED)); + U::Interrupt::pend(); + unsafe { U::Interrupt::enable() }; + + U::state().tx_rx_refcount.store(2, Ordering::Relaxed); + + Self { tx, rx } + } + + /// Adjust the baud rate to the provided value. + pub fn set_baudrate(&mut self, baudrate: Baudrate) { + let r = U::regs(); + r.baudrate().write(|w| w.set_baudrate(baudrate)); + } + + /// Split the UART in reader and writer parts. + /// + /// This allows reading and writing concurrently from independent tasks. + pub fn split(self) -> (BufferedUarteRx<'d, U>, BufferedUarteTx<'d, U>) { + (self.rx, self.tx) + } + + /// Split the UART in reader and writer parts, by reference. + /// + /// The returned halves borrow from `self`, so you can drop them and go back to using + /// the "un-split" `self`. This allows temporarily splitting the UART. + pub fn split_by_ref(&mut self) -> (&mut BufferedUarteRx<'d, U>, &mut BufferedUarteTx<'d, U>) { + (&mut self.rx, &mut self.tx) + } + + /// 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 { + self.rx.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.rx.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.rx.consume(amt) + } + + /// Write a buffer into this writer, returning how many bytes were written. + pub async fn write(&mut self, buf: &[u8]) -> Result { + self.tx.write(buf).await + } + + /// Try writing a buffer without waiting, returning how many bytes were written. + pub fn try_write(&mut self, buf: &[u8]) -> Result { + self.tx.try_write(buf) + } + + /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + pub async fn flush(&mut self) -> Result<(), Error> { + self.tx.flush().await + } +} + +/// Reader part of the buffered UARTE driver. +pub struct BufferedUarteTx<'d, U: UarteInstance> { + _peri: Peri<'d, U>, +} + +impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> { + /// Create a new BufferedUarteTx without hardware flow control. + pub fn new( + uarte: Peri<'d, U>, + txd: Peri<'d, impl GpioPin>, + _irq: impl interrupt::typelevel::Binding> + 'd, + config: Config, + tx_buffer: &'d mut [u8], + ) -> Self { + Self::new_inner(uarte, txd.into(), None, config, tx_buffer) + } + + /// Create a new BufferedUarte with hardware flow control (RTS/CTS) + pub fn new_with_cts( + uarte: Peri<'d, U>, + txd: Peri<'d, impl GpioPin>, + cts: Peri<'d, impl GpioPin>, + _irq: impl interrupt::typelevel::Binding> + 'd, + config: Config, + tx_buffer: &'d mut [u8], + ) -> Self { + Self::new_inner(uarte, txd.into(), Some(cts.into()), config, tx_buffer) + } + + fn new_inner( + peri: Peri<'d, U>, + txd: Peri<'d, AnyPin>, + cts: Option>, + config: Config, + tx_buffer: &'d mut [u8], + ) -> Self { + configure(U::regs(), config, cts.is_some()); + + let this = Self::new_innerer(peri, txd, cts, tx_buffer); + + U::regs().enable().write(|w| w.set_enable(vals::Enable::ENABLED)); + U::Interrupt::pend(); + unsafe { U::Interrupt::enable() }; + + U::state().tx_rx_refcount.store(1, Ordering::Relaxed); + + this + } + + fn new_innerer( + peri: Peri<'d, U>, + txd: Peri<'d, AnyPin>, + cts: Option>, + tx_buffer: &'d mut [u8], + ) -> Self { + let r = U::regs(); + + configure_tx_pins(r, txd, cts); + + // Initialize state + let s = U::buffered_state(); + s.tx_count.store(0, Ordering::Relaxed); + let len = tx_buffer.len(); + unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + + r.events_dma().tx().ready().write_value(0); + + // Enable interrupts + r.intenset().write(|w| { + w.set_dmatxend(true); + }); + + Self { _peri: peri } + } + + /// Write a buffer into this writer, returning how many bytes were written. + pub fn write<'a>(&'a mut self, buf: &'a [u8]) -> impl Future> + 'a { + poll_fn(move |cx| { + //trace!("poll_write: {:?}", buf.len()); + let ss = U::state(); + let s = U::buffered_state(); + let mut tx = unsafe { s.tx_buf.writer() }; + + let tx_buf = tx.push_slice(); + if tx_buf.is_empty() { + //trace!("poll_write: pending"); + ss.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + 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); + U::Interrupt::pend(); + + Poll::Ready(Ok(n)) + }) + } + + /// Try writing a buffer without waiting, returning how many bytes were written. + pub fn try_write(&mut self, buf: &[u8]) -> Result { + //trace!("poll_write: {:?}", buf.len()); + let s = U::buffered_state(); + let mut tx = unsafe { s.tx_buf.writer() }; + + let tx_buf = tx.push_slice(); + if tx_buf.is_empty() { + return Ok(0); + } + + 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); + U::Interrupt::pend(); + + Ok(n) + } + + /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + pub fn flush(&mut self) -> impl Future> + '_ { + poll_fn(move |cx| { + //trace!("poll_flush"); + let ss = U::state(); + let s = U::buffered_state(); + if !s.tx_buf.is_empty() { + //trace!("poll_flush: pending"); + ss.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + } +} + +impl<'a, U: UarteInstance> Drop for BufferedUarteTx<'a, U> { + fn drop(&mut self) { + let r = U::regs(); + + r.intenclr().write(|w| { + w.set_txdrdy(true); + w.set_dmatxready(true); + w.set_txstopped(true); + }); + r.events_txstopped().write_value(0); + r.tasks_dma().tx().stop().write_value(1); + while r.events_txstopped().read() == 0 {} + + let s = U::buffered_state(); + unsafe { s.tx_buf.deinit() } + + let s = U::state(); + drop_tx_rx(r, s); + } +} + +/// Reader part of the buffered UARTE driver. +pub struct BufferedUarteRx<'d, U: UarteInstance> { + _peri: Peri<'d, U>, +} + +impl<'d, U: UarteInstance> BufferedUarteRx<'d, U> { + /// Create a new BufferedUarte without hardware flow control. + #[allow(clippy::too_many_arguments)] + pub fn new( + uarte: Peri<'d, U>, + _irq: impl interrupt::typelevel::Binding> + 'd, + rxd: Peri<'d, impl GpioPin>, + config: Config, + rx_buffer: &'d mut [u8], + ) -> Self { + Self::new_inner(uarte, rxd.into(), None, config, rx_buffer) + } + + /// Create a new BufferedUarte with hardware flow control (RTS/CTS) + #[allow(clippy::too_many_arguments)] + pub fn new_with_rts( + uarte: Peri<'d, U>, + rxd: Peri<'d, impl GpioPin>, + rts: Peri<'d, impl GpioPin>, + _irq: impl interrupt::typelevel::Binding> + 'd, + config: Config, + rx_buffer: &'d mut [u8], + ) -> Self { + Self::new_inner(uarte, rxd.into(), Some(rts.into()), config, rx_buffer) + } + + #[allow(clippy::too_many_arguments)] + fn new_inner( + peri: Peri<'d, U>, + rxd: Peri<'d, AnyPin>, + rts: Option>, + config: Config, + rx_buffer: &'d mut [u8], + ) -> Self { + configure(U::regs(), config, rts.is_some()); + + let this = Self::new_innerer(peri, rxd, rts, rx_buffer); + + U::regs().enable().write(|w| w.set_enable(vals::Enable::ENABLED)); + U::Interrupt::pend(); + unsafe { U::Interrupt::enable() }; + + U::state().tx_rx_refcount.store(1, Ordering::Relaxed); + + this + } + + #[allow(clippy::too_many_arguments)] + fn new_innerer( + peri: Peri<'d, U>, + rxd: Peri<'d, AnyPin>, + rts: Option>, + rx_buffer: &'d mut [u8], + ) -> Self { + let r = U::regs(); + + configure_rx_pins(r, rxd, rts); + + // Initialize state + let s = U::buffered_state(); + let rx_len = rx_buffer.len().min(EASY_DMA_SIZE * 2); + let rx_ptr = rx_buffer.as_mut_ptr(); + unsafe { s.rx_buf.init(rx_ptr, rx_len) }; + + // clear errors + let errors = r.errorsrc().read(); + r.errorsrc().write_value(errors); + + r.events_error().write_value(0); + r.events_dma().rx().end().write_value(0); + + // set timeout-to-stop short + r.shorts().write(|w| { + w.set_frametimeout_dma_rx_stop(true); + }); + + // set default timeout + r.frametimeout().write_value(pac::uarte::regs::Frametimeout(0x10)); + + // Enable interrupts + r.intenset().write(|w| { + w.set_dmatxend(true); + w.set_error(true); + w.set_dmarxend(true); + }); + + Self { _peri: peri } + } + + /// 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 { + let data = self.fill_buf().await?; + let n = data.len().min(buf.len()); + buf[..n].copy_from_slice(&data[..n]); + self.consume(n); + Ok(n) + } + + /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + pub fn fill_buf(&mut self) -> impl Future> { + poll_fn(move |cx| { + compiler_fence(Ordering::SeqCst); + //trace!("poll_read"); + + let s = U::buffered_state(); + let ss = U::state(); + let mut rx = unsafe { s.rx_buf.reader() }; + + let (ptr, n) = rx.pop_buf(); + if n == 0 { + //trace!(" empty"); + ss.rx_waker.register(cx.waker()); + Poll::Pending + } else { + Poll::Ready(Ok(unsafe { slice::from_raw_parts(ptr, n) })) + } + }) + } + + /// 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) { + if amt == 0 { + return; + } + + let s = U::buffered_state(); + let mut rx = unsafe { s.rx_buf.reader() }; + rx.pop_done(amt); + } + + /// we are ready to read if there is data in the buffer + fn read_ready() -> Result { + let state = U::buffered_state(); + Ok(!state.rx_buf.is_empty()) + } +} + +impl<'a, U: UarteInstance> Drop for BufferedUarteRx<'a, U> { + fn drop(&mut self) { + let r = U::regs(); + + r.intenclr().write(|w| { + w.set_rxto(true); + }); + r.events_rxto().write_value(0); + + let s = U::buffered_state(); + unsafe { s.rx_buf.deinit() } + + let s = U::state(); + drop_tx_rx(r, s); + } +} + +mod _embedded_io { + use super::*; + + impl embedded_io_async::Error for Error { + fn kind(&self) -> embedded_io_async::ErrorKind { + match *self {} + } + } + + impl<'d, U: UarteInstance> embedded_io_async::ErrorType for BufferedUarte<'d, U> { + type Error = Error; + } + + impl<'d, U: UarteInstance> embedded_io_async::ErrorType for BufferedUarteRx<'d, U> { + type Error = Error; + } + + impl<'d, U: UarteInstance> embedded_io_async::ErrorType for BufferedUarteTx<'d, U> { + type Error = Error; + } + + impl<'d, U: UarteInstance> embedded_io_async::Read for BufferedUarte<'d, U> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read(buf).await + } + } + + impl<'d: 'd, U: UarteInstance> embedded_io_async::Read for BufferedUarteRx<'d, U> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read(buf).await + } + } + + impl<'d, U: UarteInstance> embedded_io_async::ReadReady for BufferedUarte<'d, U> { + fn read_ready(&mut self) -> Result { + BufferedUarteRx::<'d, U>::read_ready() + } + } + + impl<'d, U: UarteInstance> embedded_io_async::ReadReady for BufferedUarteRx<'d, U> { + fn read_ready(&mut self) -> Result { + Self::read_ready() + } + } + + impl<'d, U: UarteInstance> embedded_io_async::BufRead for BufferedUarte<'d, U> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.fill_buf().await + } + + fn consume(&mut self, amt: usize) { + self.consume(amt) + } + } + + impl<'d: 'd, U: UarteInstance> embedded_io_async::BufRead for BufferedUarteRx<'d, U> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.fill_buf().await + } + + fn consume(&mut self, amt: usize) { + self.consume(amt) + } + } + + impl<'d, U: UarteInstance> embedded_io_async::Write for BufferedUarte<'d, U> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write(buf).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.flush().await + } + } + + impl<'d: 'd, U: UarteInstance> embedded_io_async::Write for BufferedUarteTx<'d, U> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write(buf).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.flush().await + } + } +} diff --git a/embassy-nrf/src/chips/nrf51.rs b/embassy-nrf/src/chips/nrf51.rs index 3976e8ff0..1184c4409 100644 --- a/embassy-nrf/src/chips/nrf51.rs +++ b/embassy-nrf/src/chips/nrf51.rs @@ -115,6 +115,11 @@ impl_rtc!(RTC0, RTC0, RTC0); #[cfg(not(feature = "time-driver-rtc1"))] impl_rtc!(RTC1, RTC1, RTC1); +impl_ppi_group!(PPI_GROUP0, PPI, 0); +impl_ppi_group!(PPI_GROUP1, PPI, 1); +impl_ppi_group!(PPI_GROUP2, PPI, 2); +impl_ppi_group!(PPI_GROUP3, PPI, 3); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 63ba6999a..dd2e66927 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -195,28 +195,35 @@ impl_pin!(P0_29, 0, 29); impl_pin!(P0_30, 0, 30); impl_pin!(P0_31, 0, 31); -impl_ppi_channel!(PPI_CH0, 0 => configurable); -impl_ppi_channel!(PPI_CH1, 1 => configurable); -impl_ppi_channel!(PPI_CH2, 2 => configurable); -impl_ppi_channel!(PPI_CH3, 3 => configurable); -impl_ppi_channel!(PPI_CH4, 4 => configurable); -impl_ppi_channel!(PPI_CH5, 5 => configurable); -impl_ppi_channel!(PPI_CH6, 6 => configurable); -impl_ppi_channel!(PPI_CH7, 7 => configurable); -impl_ppi_channel!(PPI_CH8, 8 => configurable); -impl_ppi_channel!(PPI_CH9, 9 => configurable); -impl_ppi_channel!(PPI_CH20, 20 => static); -impl_ppi_channel!(PPI_CH21, 21 => static); -impl_ppi_channel!(PPI_CH22, 22 => static); -impl_ppi_channel!(PPI_CH23, 23 => static); -impl_ppi_channel!(PPI_CH24, 24 => static); -impl_ppi_channel!(PPI_CH25, 25 => static); -impl_ppi_channel!(PPI_CH26, 26 => static); -impl_ppi_channel!(PPI_CH27, 27 => static); -impl_ppi_channel!(PPI_CH28, 28 => static); -impl_ppi_channel!(PPI_CH29, 29 => static); -impl_ppi_channel!(PPI_CH30, 30 => static); -impl_ppi_channel!(PPI_CH31, 31 => static); +impl_ppi_channel!(PPI_CH0, PPI, 0 => configurable); +impl_ppi_channel!(PPI_CH1, PPI, 1 => configurable); +impl_ppi_channel!(PPI_CH2, PPI, 2 => configurable); +impl_ppi_channel!(PPI_CH3, PPI, 3 => configurable); +impl_ppi_channel!(PPI_CH4, PPI, 4 => configurable); +impl_ppi_channel!(PPI_CH5, PPI, 5 => configurable); +impl_ppi_channel!(PPI_CH6, PPI, 6 => configurable); +impl_ppi_channel!(PPI_CH7, PPI, 7 => configurable); +impl_ppi_channel!(PPI_CH8, PPI, 8 => configurable); +impl_ppi_channel!(PPI_CH9, PPI, 9 => configurable); +impl_ppi_channel!(PPI_CH20, PPI, 20 => static); +impl_ppi_channel!(PPI_CH21, PPI, 21 => static); +impl_ppi_channel!(PPI_CH22, PPI, 22 => static); +impl_ppi_channel!(PPI_CH23, PPI, 23 => static); +impl_ppi_channel!(PPI_CH24, PPI, 24 => static); +impl_ppi_channel!(PPI_CH25, PPI, 25 => static); +impl_ppi_channel!(PPI_CH26, PPI, 26 => static); +impl_ppi_channel!(PPI_CH27, PPI, 27 => static); +impl_ppi_channel!(PPI_CH28, PPI, 28 => static); +impl_ppi_channel!(PPI_CH29, PPI, 29 => static); +impl_ppi_channel!(PPI_CH30, PPI, 30 => static); +impl_ppi_channel!(PPI_CH31, PPI, 31 => static); + +impl_ppi_group!(PPI_GROUP0, PPI, 0); +impl_ppi_group!(PPI_GROUP1, PPI, 1); +impl_ppi_group!(PPI_GROUP2, PPI, 2); +impl_ppi_group!(PPI_GROUP3, PPI, 3); +impl_ppi_group!(PPI_GROUP4, PPI, 4); +impl_ppi_group!(PPI_GROUP5, PPI, 5); impl_saadc_input!(P0_04, ANALOG_INPUT2); impl_saadc_input!(P0_05, ANALOG_INPUT3); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 7f744f9fb..7acb53a03 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -205,38 +205,45 @@ impl_pin!(P0_29, 0, 29); impl_pin!(P0_30, 0, 30); impl_pin!(P0_31, 0, 31); -impl_ppi_channel!(PPI_CH0, 0 => configurable); -impl_ppi_channel!(PPI_CH1, 1 => configurable); -impl_ppi_channel!(PPI_CH2, 2 => configurable); -impl_ppi_channel!(PPI_CH3, 3 => configurable); -impl_ppi_channel!(PPI_CH4, 4 => configurable); -impl_ppi_channel!(PPI_CH5, 5 => configurable); -impl_ppi_channel!(PPI_CH6, 6 => configurable); -impl_ppi_channel!(PPI_CH7, 7 => configurable); -impl_ppi_channel!(PPI_CH8, 8 => configurable); -impl_ppi_channel!(PPI_CH9, 9 => configurable); -impl_ppi_channel!(PPI_CH10, 10 => configurable); -impl_ppi_channel!(PPI_CH11, 11 => configurable); -impl_ppi_channel!(PPI_CH12, 12 => configurable); -impl_ppi_channel!(PPI_CH13, 13 => configurable); -impl_ppi_channel!(PPI_CH14, 14 => configurable); -impl_ppi_channel!(PPI_CH15, 15 => configurable); -impl_ppi_channel!(PPI_CH16, 16 => configurable); -impl_ppi_channel!(PPI_CH17, 17 => configurable); -impl_ppi_channel!(PPI_CH18, 18 => configurable); -impl_ppi_channel!(PPI_CH19, 19 => configurable); -impl_ppi_channel!(PPI_CH20, 20 => static); -impl_ppi_channel!(PPI_CH21, 21 => static); -impl_ppi_channel!(PPI_CH22, 22 => static); -impl_ppi_channel!(PPI_CH23, 23 => static); -impl_ppi_channel!(PPI_CH24, 24 => static); -impl_ppi_channel!(PPI_CH25, 25 => static); -impl_ppi_channel!(PPI_CH26, 26 => static); -impl_ppi_channel!(PPI_CH27, 27 => static); -impl_ppi_channel!(PPI_CH28, 28 => static); -impl_ppi_channel!(PPI_CH29, 29 => static); -impl_ppi_channel!(PPI_CH30, 30 => static); -impl_ppi_channel!(PPI_CH31, 31 => static); +impl_ppi_channel!(PPI_CH0, PPI, 0 => configurable); +impl_ppi_channel!(PPI_CH1, PPI, 1 => configurable); +impl_ppi_channel!(PPI_CH2, PPI, 2 => configurable); +impl_ppi_channel!(PPI_CH3, PPI, 3 => configurable); +impl_ppi_channel!(PPI_CH4, PPI, 4 => configurable); +impl_ppi_channel!(PPI_CH5, PPI, 5 => configurable); +impl_ppi_channel!(PPI_CH6, PPI, 6 => configurable); +impl_ppi_channel!(PPI_CH7, PPI, 7 => configurable); +impl_ppi_channel!(PPI_CH8, PPI, 8 => configurable); +impl_ppi_channel!(PPI_CH9, PPI, 9 => configurable); +impl_ppi_channel!(PPI_CH10, PPI, 10 => configurable); +impl_ppi_channel!(PPI_CH11, PPI, 11 => configurable); +impl_ppi_channel!(PPI_CH12, PPI, 12 => configurable); +impl_ppi_channel!(PPI_CH13, PPI, 13 => configurable); +impl_ppi_channel!(PPI_CH14, PPI, 14 => configurable); +impl_ppi_channel!(PPI_CH15, PPI, 15 => configurable); +impl_ppi_channel!(PPI_CH16, PPI, 16 => configurable); +impl_ppi_channel!(PPI_CH17, PPI, 17 => configurable); +impl_ppi_channel!(PPI_CH18, PPI, 18 => configurable); +impl_ppi_channel!(PPI_CH19, PPI, 19 => configurable); +impl_ppi_channel!(PPI_CH20, PPI, 20 => static); +impl_ppi_channel!(PPI_CH21, PPI, 21 => static); +impl_ppi_channel!(PPI_CH22, PPI, 22 => static); +impl_ppi_channel!(PPI_CH23, PPI, 23 => static); +impl_ppi_channel!(PPI_CH24, PPI, 24 => static); +impl_ppi_channel!(PPI_CH25, PPI, 25 => static); +impl_ppi_channel!(PPI_CH26, PPI, 26 => static); +impl_ppi_channel!(PPI_CH27, PPI, 27 => static); +impl_ppi_channel!(PPI_CH28, PPI, 28 => static); +impl_ppi_channel!(PPI_CH29, PPI, 29 => static); +impl_ppi_channel!(PPI_CH30, PPI, 30 => static); +impl_ppi_channel!(PPI_CH31, PPI, 31 => static); + +impl_ppi_group!(PPI_GROUP0, PPI, 0); +impl_ppi_group!(PPI_GROUP1, PPI, 1); +impl_ppi_group!(PPI_GROUP2, PPI, 2); +impl_ppi_group!(PPI_GROUP3, PPI, 3); +impl_ppi_group!(PPI_GROUP4, PPI, 4); +impl_ppi_group!(PPI_GROUP5, PPI, 5); impl_saadc_input!(P0_02, ANALOG_INPUT0); impl_saadc_input!(P0_03, ANALOG_INPUT1); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 908167e31..4178ef6cd 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -207,38 +207,45 @@ impl_pin!(P0_29, 0, 29); impl_pin!(P0_30, 0, 30); impl_pin!(P0_31, 0, 31); -impl_ppi_channel!(PPI_CH0, 0 => configurable); -impl_ppi_channel!(PPI_CH1, 1 => configurable); -impl_ppi_channel!(PPI_CH2, 2 => configurable); -impl_ppi_channel!(PPI_CH3, 3 => configurable); -impl_ppi_channel!(PPI_CH4, 4 => configurable); -impl_ppi_channel!(PPI_CH5, 5 => configurable); -impl_ppi_channel!(PPI_CH6, 6 => configurable); -impl_ppi_channel!(PPI_CH7, 7 => configurable); -impl_ppi_channel!(PPI_CH8, 8 => configurable); -impl_ppi_channel!(PPI_CH9, 9 => configurable); -impl_ppi_channel!(PPI_CH10, 10 => configurable); -impl_ppi_channel!(PPI_CH11, 11 => configurable); -impl_ppi_channel!(PPI_CH12, 12 => configurable); -impl_ppi_channel!(PPI_CH13, 13 => configurable); -impl_ppi_channel!(PPI_CH14, 14 => configurable); -impl_ppi_channel!(PPI_CH15, 15 => configurable); -impl_ppi_channel!(PPI_CH16, 16 => configurable); -impl_ppi_channel!(PPI_CH17, 17 => configurable); -impl_ppi_channel!(PPI_CH18, 18 => configurable); -impl_ppi_channel!(PPI_CH19, 19 => configurable); -impl_ppi_channel!(PPI_CH20, 20 => static); -impl_ppi_channel!(PPI_CH21, 21 => static); -impl_ppi_channel!(PPI_CH22, 22 => static); -impl_ppi_channel!(PPI_CH23, 23 => static); -impl_ppi_channel!(PPI_CH24, 24 => static); -impl_ppi_channel!(PPI_CH25, 25 => static); -impl_ppi_channel!(PPI_CH26, 26 => static); -impl_ppi_channel!(PPI_CH27, 27 => static); -impl_ppi_channel!(PPI_CH28, 28 => static); -impl_ppi_channel!(PPI_CH29, 29 => static); -impl_ppi_channel!(PPI_CH30, 30 => static); -impl_ppi_channel!(PPI_CH31, 31 => static); +impl_ppi_channel!(PPI_CH0, PPI, 0 => configurable); +impl_ppi_channel!(PPI_CH1, PPI, 1 => configurable); +impl_ppi_channel!(PPI_CH2, PPI, 2 => configurable); +impl_ppi_channel!(PPI_CH3, PPI, 3 => configurable); +impl_ppi_channel!(PPI_CH4, PPI, 4 => configurable); +impl_ppi_channel!(PPI_CH5, PPI, 5 => configurable); +impl_ppi_channel!(PPI_CH6, PPI, 6 => configurable); +impl_ppi_channel!(PPI_CH7, PPI, 7 => configurable); +impl_ppi_channel!(PPI_CH8, PPI, 8 => configurable); +impl_ppi_channel!(PPI_CH9, PPI, 9 => configurable); +impl_ppi_channel!(PPI_CH10, PPI, 10 => configurable); +impl_ppi_channel!(PPI_CH11, PPI, 11 => configurable); +impl_ppi_channel!(PPI_CH12, PPI, 12 => configurable); +impl_ppi_channel!(PPI_CH13, PPI, 13 => configurable); +impl_ppi_channel!(PPI_CH14, PPI, 14 => configurable); +impl_ppi_channel!(PPI_CH15, PPI, 15 => configurable); +impl_ppi_channel!(PPI_CH16, PPI, 16 => configurable); +impl_ppi_channel!(PPI_CH17, PPI, 17 => configurable); +impl_ppi_channel!(PPI_CH18, PPI, 18 => configurable); +impl_ppi_channel!(PPI_CH19, PPI, 19 => configurable); +impl_ppi_channel!(PPI_CH20, PPI, 20 => static); +impl_ppi_channel!(PPI_CH21, PPI, 21 => static); +impl_ppi_channel!(PPI_CH22, PPI, 22 => static); +impl_ppi_channel!(PPI_CH23, PPI, 23 => static); +impl_ppi_channel!(PPI_CH24, PPI, 24 => static); +impl_ppi_channel!(PPI_CH25, PPI, 25 => static); +impl_ppi_channel!(PPI_CH26, PPI, 26 => static); +impl_ppi_channel!(PPI_CH27, PPI, 27 => static); +impl_ppi_channel!(PPI_CH28, PPI, 28 => static); +impl_ppi_channel!(PPI_CH29, PPI, 29 => static); +impl_ppi_channel!(PPI_CH30, PPI, 30 => static); +impl_ppi_channel!(PPI_CH31, PPI, 31 => static); + +impl_ppi_group!(PPI_GROUP0, PPI, 0); +impl_ppi_group!(PPI_GROUP1, PPI, 1); +impl_ppi_group!(PPI_GROUP2, PPI, 2); +impl_ppi_group!(PPI_GROUP3, PPI, 3); +impl_ppi_group!(PPI_GROUP4, PPI, 4); +impl_ppi_group!(PPI_GROUP5, PPI, 5); impl_saadc_input!(P0_02, ANALOG_INPUT0); impl_saadc_input!(P0_03, ANALOG_INPUT1); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 22360575b..32304b3ea 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -207,38 +207,45 @@ impl_pin!(P0_29, 0, 29); impl_pin!(P0_30, 0, 30); impl_pin!(P0_31, 0, 31); -impl_ppi_channel!(PPI_CH0, 0 => configurable); -impl_ppi_channel!(PPI_CH1, 1 => configurable); -impl_ppi_channel!(PPI_CH2, 2 => configurable); -impl_ppi_channel!(PPI_CH3, 3 => configurable); -impl_ppi_channel!(PPI_CH4, 4 => configurable); -impl_ppi_channel!(PPI_CH5, 5 => configurable); -impl_ppi_channel!(PPI_CH6, 6 => configurable); -impl_ppi_channel!(PPI_CH7, 7 => configurable); -impl_ppi_channel!(PPI_CH8, 8 => configurable); -impl_ppi_channel!(PPI_CH9, 9 => configurable); -impl_ppi_channel!(PPI_CH10, 10 => configurable); -impl_ppi_channel!(PPI_CH11, 11 => configurable); -impl_ppi_channel!(PPI_CH12, 12 => configurable); -impl_ppi_channel!(PPI_CH13, 13 => configurable); -impl_ppi_channel!(PPI_CH14, 14 => configurable); -impl_ppi_channel!(PPI_CH15, 15 => configurable); -impl_ppi_channel!(PPI_CH16, 16 => configurable); -impl_ppi_channel!(PPI_CH17, 17 => configurable); -impl_ppi_channel!(PPI_CH18, 18 => configurable); -impl_ppi_channel!(PPI_CH19, 19 => configurable); -impl_ppi_channel!(PPI_CH20, 20 => static); -impl_ppi_channel!(PPI_CH21, 21 => static); -impl_ppi_channel!(PPI_CH22, 22 => static); -impl_ppi_channel!(PPI_CH23, 23 => static); -impl_ppi_channel!(PPI_CH24, 24 => static); -impl_ppi_channel!(PPI_CH25, 25 => static); -impl_ppi_channel!(PPI_CH26, 26 => static); -impl_ppi_channel!(PPI_CH27, 27 => static); -impl_ppi_channel!(PPI_CH28, 28 => static); -impl_ppi_channel!(PPI_CH29, 29 => static); -impl_ppi_channel!(PPI_CH30, 30 => static); -impl_ppi_channel!(PPI_CH31, 31 => static); +impl_ppi_channel!(PPI_CH0, PPI, 0 => configurable); +impl_ppi_channel!(PPI_CH1, PPI, 1 => configurable); +impl_ppi_channel!(PPI_CH2, PPI, 2 => configurable); +impl_ppi_channel!(PPI_CH3, PPI, 3 => configurable); +impl_ppi_channel!(PPI_CH4, PPI, 4 => configurable); +impl_ppi_channel!(PPI_CH5, PPI, 5 => configurable); +impl_ppi_channel!(PPI_CH6, PPI, 6 => configurable); +impl_ppi_channel!(PPI_CH7, PPI, 7 => configurable); +impl_ppi_channel!(PPI_CH8, PPI, 8 => configurable); +impl_ppi_channel!(PPI_CH9, PPI, 9 => configurable); +impl_ppi_channel!(PPI_CH10, PPI, 10 => configurable); +impl_ppi_channel!(PPI_CH11, PPI, 11 => configurable); +impl_ppi_channel!(PPI_CH12, PPI, 12 => configurable); +impl_ppi_channel!(PPI_CH13, PPI, 13 => configurable); +impl_ppi_channel!(PPI_CH14, PPI, 14 => configurable); +impl_ppi_channel!(PPI_CH15, PPI, 15 => configurable); +impl_ppi_channel!(PPI_CH16, PPI, 16 => configurable); +impl_ppi_channel!(PPI_CH17, PPI, 17 => configurable); +impl_ppi_channel!(PPI_CH18, PPI, 18 => configurable); +impl_ppi_channel!(PPI_CH19, PPI, 19 => configurable); +impl_ppi_channel!(PPI_CH20, PPI, 20 => static); +impl_ppi_channel!(PPI_CH21, PPI, 21 => static); +impl_ppi_channel!(PPI_CH22, PPI, 22 => static); +impl_ppi_channel!(PPI_CH23, PPI, 23 => static); +impl_ppi_channel!(PPI_CH24, PPI, 24 => static); +impl_ppi_channel!(PPI_CH25, PPI, 25 => static); +impl_ppi_channel!(PPI_CH26, PPI, 26 => static); +impl_ppi_channel!(PPI_CH27, PPI, 27 => static); +impl_ppi_channel!(PPI_CH28, PPI, 28 => static); +impl_ppi_channel!(PPI_CH29, PPI, 29 => static); +impl_ppi_channel!(PPI_CH30, PPI, 30 => static); +impl_ppi_channel!(PPI_CH31, PPI, 31 => static); + +impl_ppi_group!(PPI_GROUP0, PPI, 0); +impl_ppi_group!(PPI_GROUP1, PPI, 1); +impl_ppi_group!(PPI_GROUP2, PPI, 2); +impl_ppi_group!(PPI_GROUP3, PPI, 3); +impl_ppi_group!(PPI_GROUP4, PPI, 4); +impl_ppi_group!(PPI_GROUP5, PPI, 5); impl_radio!(RADIO, RADIO, RADIO); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 1598df3fe..06363a467 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -240,38 +240,45 @@ impl_pin!(P0_29, 0, 29); impl_pin!(P0_30, 0, 30); impl_pin!(P0_31, 0, 31); -impl_ppi_channel!(PPI_CH0, 0 => configurable); -impl_ppi_channel!(PPI_CH1, 1 => configurable); -impl_ppi_channel!(PPI_CH2, 2 => configurable); -impl_ppi_channel!(PPI_CH3, 3 => configurable); -impl_ppi_channel!(PPI_CH4, 4 => configurable); -impl_ppi_channel!(PPI_CH5, 5 => configurable); -impl_ppi_channel!(PPI_CH6, 6 => configurable); -impl_ppi_channel!(PPI_CH7, 7 => configurable); -impl_ppi_channel!(PPI_CH8, 8 => configurable); -impl_ppi_channel!(PPI_CH9, 9 => configurable); -impl_ppi_channel!(PPI_CH10, 10 => configurable); -impl_ppi_channel!(PPI_CH11, 11 => configurable); -impl_ppi_channel!(PPI_CH12, 12 => configurable); -impl_ppi_channel!(PPI_CH13, 13 => configurable); -impl_ppi_channel!(PPI_CH14, 14 => configurable); -impl_ppi_channel!(PPI_CH15, 15 => configurable); -impl_ppi_channel!(PPI_CH16, 16 => configurable); -impl_ppi_channel!(PPI_CH17, 17 => configurable); -impl_ppi_channel!(PPI_CH18, 18 => configurable); -impl_ppi_channel!(PPI_CH19, 19 => configurable); -impl_ppi_channel!(PPI_CH20, 20 => static); -impl_ppi_channel!(PPI_CH21, 21 => static); -impl_ppi_channel!(PPI_CH22, 22 => static); -impl_ppi_channel!(PPI_CH23, 23 => static); -impl_ppi_channel!(PPI_CH24, 24 => static); -impl_ppi_channel!(PPI_CH25, 25 => static); -impl_ppi_channel!(PPI_CH26, 26 => static); -impl_ppi_channel!(PPI_CH27, 27 => static); -impl_ppi_channel!(PPI_CH28, 28 => static); -impl_ppi_channel!(PPI_CH29, 29 => static); -impl_ppi_channel!(PPI_CH30, 30 => static); -impl_ppi_channel!(PPI_CH31, 31 => static); +impl_ppi_channel!(PPI_CH0, PPI, 0 => configurable); +impl_ppi_channel!(PPI_CH1, PPI, 1 => configurable); +impl_ppi_channel!(PPI_CH2, PPI, 2 => configurable); +impl_ppi_channel!(PPI_CH3, PPI, 3 => configurable); +impl_ppi_channel!(PPI_CH4, PPI, 4 => configurable); +impl_ppi_channel!(PPI_CH5, PPI, 5 => configurable); +impl_ppi_channel!(PPI_CH6, PPI, 6 => configurable); +impl_ppi_channel!(PPI_CH7, PPI, 7 => configurable); +impl_ppi_channel!(PPI_CH8, PPI, 8 => configurable); +impl_ppi_channel!(PPI_CH9, PPI, 9 => configurable); +impl_ppi_channel!(PPI_CH10, PPI, 10 => configurable); +impl_ppi_channel!(PPI_CH11, PPI, 11 => configurable); +impl_ppi_channel!(PPI_CH12, PPI, 12 => configurable); +impl_ppi_channel!(PPI_CH13, PPI, 13 => configurable); +impl_ppi_channel!(PPI_CH14, PPI, 14 => configurable); +impl_ppi_channel!(PPI_CH15, PPI, 15 => configurable); +impl_ppi_channel!(PPI_CH16, PPI, 16 => configurable); +impl_ppi_channel!(PPI_CH17, PPI, 17 => configurable); +impl_ppi_channel!(PPI_CH18, PPI, 18 => configurable); +impl_ppi_channel!(PPI_CH19, PPI, 19 => configurable); +impl_ppi_channel!(PPI_CH20, PPI, 20 => static); +impl_ppi_channel!(PPI_CH21, PPI, 21 => static); +impl_ppi_channel!(PPI_CH22, PPI, 22 => static); +impl_ppi_channel!(PPI_CH23, PPI, 23 => static); +impl_ppi_channel!(PPI_CH24, PPI, 24 => static); +impl_ppi_channel!(PPI_CH25, PPI, 25 => static); +impl_ppi_channel!(PPI_CH26, PPI, 26 => static); +impl_ppi_channel!(PPI_CH27, PPI, 27 => static); +impl_ppi_channel!(PPI_CH28, PPI, 28 => static); +impl_ppi_channel!(PPI_CH29, PPI, 29 => static); +impl_ppi_channel!(PPI_CH30, PPI, 30 => static); +impl_ppi_channel!(PPI_CH31, PPI, 31 => static); + +impl_ppi_group!(PPI_GROUP0, PPI, 0); +impl_ppi_group!(PPI_GROUP1, PPI, 1); +impl_ppi_group!(PPI_GROUP2, PPI, 2); +impl_ppi_group!(PPI_GROUP3, PPI, 3); +impl_ppi_group!(PPI_GROUP4, PPI, 4); +impl_ppi_group!(PPI_GROUP5, PPI, 5); impl_saadc_input!(P0_02, ANALOG_INPUT0); impl_saadc_input!(P0_03, ANALOG_INPUT1); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 6931fb064..754943d33 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -282,38 +282,45 @@ impl_pin!(P1_13, 1, 13); impl_pin!(P1_14, 1, 14); impl_pin!(P1_15, 1, 15); -impl_ppi_channel!(PPI_CH0, 0 => configurable); -impl_ppi_channel!(PPI_CH1, 1 => configurable); -impl_ppi_channel!(PPI_CH2, 2 => configurable); -impl_ppi_channel!(PPI_CH3, 3 => configurable); -impl_ppi_channel!(PPI_CH4, 4 => configurable); -impl_ppi_channel!(PPI_CH5, 5 => configurable); -impl_ppi_channel!(PPI_CH6, 6 => configurable); -impl_ppi_channel!(PPI_CH7, 7 => configurable); -impl_ppi_channel!(PPI_CH8, 8 => configurable); -impl_ppi_channel!(PPI_CH9, 9 => configurable); -impl_ppi_channel!(PPI_CH10, 10 => configurable); -impl_ppi_channel!(PPI_CH11, 11 => configurable); -impl_ppi_channel!(PPI_CH12, 12 => configurable); -impl_ppi_channel!(PPI_CH13, 13 => configurable); -impl_ppi_channel!(PPI_CH14, 14 => configurable); -impl_ppi_channel!(PPI_CH15, 15 => configurable); -impl_ppi_channel!(PPI_CH16, 16 => configurable); -impl_ppi_channel!(PPI_CH17, 17 => configurable); -impl_ppi_channel!(PPI_CH18, 18 => configurable); -impl_ppi_channel!(PPI_CH19, 19 => configurable); -impl_ppi_channel!(PPI_CH20, 20 => static); -impl_ppi_channel!(PPI_CH21, 21 => static); -impl_ppi_channel!(PPI_CH22, 22 => static); -impl_ppi_channel!(PPI_CH23, 23 => static); -impl_ppi_channel!(PPI_CH24, 24 => static); -impl_ppi_channel!(PPI_CH25, 25 => static); -impl_ppi_channel!(PPI_CH26, 26 => static); -impl_ppi_channel!(PPI_CH27, 27 => static); -impl_ppi_channel!(PPI_CH28, 28 => static); -impl_ppi_channel!(PPI_CH29, 29 => static); -impl_ppi_channel!(PPI_CH30, 30 => static); -impl_ppi_channel!(PPI_CH31, 31 => static); +impl_ppi_channel!(PPI_CH0, PPI, 0 => configurable); +impl_ppi_channel!(PPI_CH1, PPI, 1 => configurable); +impl_ppi_channel!(PPI_CH2, PPI, 2 => configurable); +impl_ppi_channel!(PPI_CH3, PPI, 3 => configurable); +impl_ppi_channel!(PPI_CH4, PPI, 4 => configurable); +impl_ppi_channel!(PPI_CH5, PPI, 5 => configurable); +impl_ppi_channel!(PPI_CH6, PPI, 6 => configurable); +impl_ppi_channel!(PPI_CH7, PPI, 7 => configurable); +impl_ppi_channel!(PPI_CH8, PPI, 8 => configurable); +impl_ppi_channel!(PPI_CH9, PPI, 9 => configurable); +impl_ppi_channel!(PPI_CH10, PPI, 10 => configurable); +impl_ppi_channel!(PPI_CH11, PPI, 11 => configurable); +impl_ppi_channel!(PPI_CH12, PPI, 12 => configurable); +impl_ppi_channel!(PPI_CH13, PPI, 13 => configurable); +impl_ppi_channel!(PPI_CH14, PPI, 14 => configurable); +impl_ppi_channel!(PPI_CH15, PPI, 15 => configurable); +impl_ppi_channel!(PPI_CH16, PPI, 16 => configurable); +impl_ppi_channel!(PPI_CH17, PPI, 17 => configurable); +impl_ppi_channel!(PPI_CH18, PPI, 18 => configurable); +impl_ppi_channel!(PPI_CH19, PPI, 19 => configurable); +impl_ppi_channel!(PPI_CH20, PPI, 20 => static); +impl_ppi_channel!(PPI_CH21, PPI, 21 => static); +impl_ppi_channel!(PPI_CH22, PPI, 22 => static); +impl_ppi_channel!(PPI_CH23, PPI, 23 => static); +impl_ppi_channel!(PPI_CH24, PPI, 24 => static); +impl_ppi_channel!(PPI_CH25, PPI, 25 => static); +impl_ppi_channel!(PPI_CH26, PPI, 26 => static); +impl_ppi_channel!(PPI_CH27, PPI, 27 => static); +impl_ppi_channel!(PPI_CH28, PPI, 28 => static); +impl_ppi_channel!(PPI_CH29, PPI, 29 => static); +impl_ppi_channel!(PPI_CH30, PPI, 30 => static); +impl_ppi_channel!(PPI_CH31, PPI, 31 => static); + +impl_ppi_group!(PPI_GROUP0, PPI, 0); +impl_ppi_group!(PPI_GROUP1, PPI, 1); +impl_ppi_group!(PPI_GROUP2, PPI, 2); +impl_ppi_group!(PPI_GROUP3, PPI, 3); +impl_ppi_group!(PPI_GROUP4, PPI, 4); +impl_ppi_group!(PPI_GROUP5, PPI, 5); impl_saadc_input!(P0_02, ANALOG_INPUT0); impl_saadc_input!(P0_03, ANALOG_INPUT1); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 5fa521aae..ac07cd820 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -287,38 +287,45 @@ impl_pin!(P1_13, 1, 13); impl_pin!(P1_14, 1, 14); impl_pin!(P1_15, 1, 15); -impl_ppi_channel!(PPI_CH0, 0 => configurable); -impl_ppi_channel!(PPI_CH1, 1 => configurable); -impl_ppi_channel!(PPI_CH2, 2 => configurable); -impl_ppi_channel!(PPI_CH3, 3 => configurable); -impl_ppi_channel!(PPI_CH4, 4 => configurable); -impl_ppi_channel!(PPI_CH5, 5 => configurable); -impl_ppi_channel!(PPI_CH6, 6 => configurable); -impl_ppi_channel!(PPI_CH7, 7 => configurable); -impl_ppi_channel!(PPI_CH8, 8 => configurable); -impl_ppi_channel!(PPI_CH9, 9 => configurable); -impl_ppi_channel!(PPI_CH10, 10 => configurable); -impl_ppi_channel!(PPI_CH11, 11 => configurable); -impl_ppi_channel!(PPI_CH12, 12 => configurable); -impl_ppi_channel!(PPI_CH13, 13 => configurable); -impl_ppi_channel!(PPI_CH14, 14 => configurable); -impl_ppi_channel!(PPI_CH15, 15 => configurable); -impl_ppi_channel!(PPI_CH16, 16 => configurable); -impl_ppi_channel!(PPI_CH17, 17 => configurable); -impl_ppi_channel!(PPI_CH18, 18 => configurable); -impl_ppi_channel!(PPI_CH19, 19 => configurable); -impl_ppi_channel!(PPI_CH20, 20 => static); -impl_ppi_channel!(PPI_CH21, 21 => static); -impl_ppi_channel!(PPI_CH22, 22 => static); -impl_ppi_channel!(PPI_CH23, 23 => static); -impl_ppi_channel!(PPI_CH24, 24 => static); -impl_ppi_channel!(PPI_CH25, 25 => static); -impl_ppi_channel!(PPI_CH26, 26 => static); -impl_ppi_channel!(PPI_CH27, 27 => static); -impl_ppi_channel!(PPI_CH28, 28 => static); -impl_ppi_channel!(PPI_CH29, 29 => static); -impl_ppi_channel!(PPI_CH30, 30 => static); -impl_ppi_channel!(PPI_CH31, 31 => static); +impl_ppi_channel!(PPI_CH0, PPI, 0 => configurable); +impl_ppi_channel!(PPI_CH1, PPI, 1 => configurable); +impl_ppi_channel!(PPI_CH2, PPI, 2 => configurable); +impl_ppi_channel!(PPI_CH3, PPI, 3 => configurable); +impl_ppi_channel!(PPI_CH4, PPI, 4 => configurable); +impl_ppi_channel!(PPI_CH5, PPI, 5 => configurable); +impl_ppi_channel!(PPI_CH6, PPI, 6 => configurable); +impl_ppi_channel!(PPI_CH7, PPI, 7 => configurable); +impl_ppi_channel!(PPI_CH8, PPI, 8 => configurable); +impl_ppi_channel!(PPI_CH9, PPI, 9 => configurable); +impl_ppi_channel!(PPI_CH10, PPI, 10 => configurable); +impl_ppi_channel!(PPI_CH11, PPI, 11 => configurable); +impl_ppi_channel!(PPI_CH12, PPI, 12 => configurable); +impl_ppi_channel!(PPI_CH13, PPI, 13 => configurable); +impl_ppi_channel!(PPI_CH14, PPI, 14 => configurable); +impl_ppi_channel!(PPI_CH15, PPI, 15 => configurable); +impl_ppi_channel!(PPI_CH16, PPI, 16 => configurable); +impl_ppi_channel!(PPI_CH17, PPI, 17 => configurable); +impl_ppi_channel!(PPI_CH18, PPI, 18 => configurable); +impl_ppi_channel!(PPI_CH19, PPI, 19 => configurable); +impl_ppi_channel!(PPI_CH20, PPI, 20 => static); +impl_ppi_channel!(PPI_CH21, PPI, 21 => static); +impl_ppi_channel!(PPI_CH22, PPI, 22 => static); +impl_ppi_channel!(PPI_CH23, PPI, 23 => static); +impl_ppi_channel!(PPI_CH24, PPI, 24 => static); +impl_ppi_channel!(PPI_CH25, PPI, 25 => static); +impl_ppi_channel!(PPI_CH26, PPI, 26 => static); +impl_ppi_channel!(PPI_CH27, PPI, 27 => static); +impl_ppi_channel!(PPI_CH28, PPI, 28 => static); +impl_ppi_channel!(PPI_CH29, PPI, 29 => static); +impl_ppi_channel!(PPI_CH30, PPI, 30 => static); +impl_ppi_channel!(PPI_CH31, PPI, 31 => static); + +impl_ppi_group!(PPI_GROUP0, PPI, 0); +impl_ppi_group!(PPI_GROUP1, PPI, 1); +impl_ppi_group!(PPI_GROUP2, PPI, 2); +impl_ppi_group!(PPI_GROUP3, PPI, 3); +impl_ppi_group!(PPI_GROUP4, PPI, 4); +impl_ppi_group!(PPI_GROUP5, PPI, 5); impl_saadc_input!(P0_02, ANALOG_INPUT0); impl_saadc_input!(P0_03, ANALOG_INPUT1); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 730c9842d..aa51527fb 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -435,38 +435,45 @@ impl_pin!(P1_13, 1, 13); impl_pin!(P1_14, 1, 14); impl_pin!(P1_15, 1, 15); -impl_ppi_channel!(PPI_CH0, 0 => configurable); -impl_ppi_channel!(PPI_CH1, 1 => configurable); -impl_ppi_channel!(PPI_CH2, 2 => configurable); -impl_ppi_channel!(PPI_CH3, 3 => configurable); -impl_ppi_channel!(PPI_CH4, 4 => configurable); -impl_ppi_channel!(PPI_CH5, 5 => configurable); -impl_ppi_channel!(PPI_CH6, 6 => configurable); -impl_ppi_channel!(PPI_CH7, 7 => configurable); -impl_ppi_channel!(PPI_CH8, 8 => configurable); -impl_ppi_channel!(PPI_CH9, 9 => configurable); -impl_ppi_channel!(PPI_CH10, 10 => configurable); -impl_ppi_channel!(PPI_CH11, 11 => configurable); -impl_ppi_channel!(PPI_CH12, 12 => configurable); -impl_ppi_channel!(PPI_CH13, 13 => configurable); -impl_ppi_channel!(PPI_CH14, 14 => configurable); -impl_ppi_channel!(PPI_CH15, 15 => configurable); -impl_ppi_channel!(PPI_CH16, 16 => configurable); -impl_ppi_channel!(PPI_CH17, 17 => configurable); -impl_ppi_channel!(PPI_CH18, 18 => configurable); -impl_ppi_channel!(PPI_CH19, 19 => configurable); -impl_ppi_channel!(PPI_CH20, 20 => configurable); -impl_ppi_channel!(PPI_CH21, 21 => configurable); -impl_ppi_channel!(PPI_CH22, 22 => configurable); -impl_ppi_channel!(PPI_CH23, 23 => configurable); -impl_ppi_channel!(PPI_CH24, 24 => configurable); -impl_ppi_channel!(PPI_CH25, 25 => configurable); -impl_ppi_channel!(PPI_CH26, 26 => configurable); -impl_ppi_channel!(PPI_CH27, 27 => configurable); -impl_ppi_channel!(PPI_CH28, 28 => configurable); -impl_ppi_channel!(PPI_CH29, 29 => configurable); -impl_ppi_channel!(PPI_CH30, 30 => configurable); -impl_ppi_channel!(PPI_CH31, 31 => configurable); +impl_ppi_channel!(PPI_CH0, DPPIC, 0 => configurable); +impl_ppi_channel!(PPI_CH1, DPPIC, 1 => configurable); +impl_ppi_channel!(PPI_CH2, DPPIC, 2 => configurable); +impl_ppi_channel!(PPI_CH3, DPPIC, 3 => configurable); +impl_ppi_channel!(PPI_CH4, DPPIC, 4 => configurable); +impl_ppi_channel!(PPI_CH5, DPPIC, 5 => configurable); +impl_ppi_channel!(PPI_CH6, DPPIC, 6 => configurable); +impl_ppi_channel!(PPI_CH7, DPPIC, 7 => configurable); +impl_ppi_channel!(PPI_CH8, DPPIC, 8 => configurable); +impl_ppi_channel!(PPI_CH9, DPPIC, 9 => configurable); +impl_ppi_channel!(PPI_CH10, DPPIC, 10 => configurable); +impl_ppi_channel!(PPI_CH11, DPPIC, 11 => configurable); +impl_ppi_channel!(PPI_CH12, DPPIC, 12 => configurable); +impl_ppi_channel!(PPI_CH13, DPPIC, 13 => configurable); +impl_ppi_channel!(PPI_CH14, DPPIC, 14 => configurable); +impl_ppi_channel!(PPI_CH15, DPPIC, 15 => configurable); +impl_ppi_channel!(PPI_CH16, DPPIC, 16 => configurable); +impl_ppi_channel!(PPI_CH17, DPPIC, 17 => configurable); +impl_ppi_channel!(PPI_CH18, DPPIC, 18 => configurable); +impl_ppi_channel!(PPI_CH19, DPPIC, 19 => configurable); +impl_ppi_channel!(PPI_CH20, DPPIC, 20 => configurable); +impl_ppi_channel!(PPI_CH21, DPPIC, 21 => configurable); +impl_ppi_channel!(PPI_CH22, DPPIC, 22 => configurable); +impl_ppi_channel!(PPI_CH23, DPPIC, 23 => configurable); +impl_ppi_channel!(PPI_CH24, DPPIC, 24 => configurable); +impl_ppi_channel!(PPI_CH25, DPPIC, 25 => configurable); +impl_ppi_channel!(PPI_CH26, DPPIC, 26 => configurable); +impl_ppi_channel!(PPI_CH27, DPPIC, 27 => configurable); +impl_ppi_channel!(PPI_CH28, DPPIC, 28 => configurable); +impl_ppi_channel!(PPI_CH29, DPPIC, 29 => configurable); +impl_ppi_channel!(PPI_CH30, DPPIC, 30 => configurable); +impl_ppi_channel!(PPI_CH31, DPPIC, 31 => configurable); + +impl_ppi_group!(PPI_GROUP0, DPPIC, 0); +impl_ppi_group!(PPI_GROUP1, DPPIC, 1); +impl_ppi_group!(PPI_GROUP2, DPPIC, 2); +impl_ppi_group!(PPI_GROUP3, DPPIC, 3); +impl_ppi_group!(PPI_GROUP4, DPPIC, 4); +impl_ppi_group!(PPI_GROUP5, DPPIC, 5); impl_saadc_input!(P0_04, ANALOG_INPUT0); impl_saadc_input!(P0_05, ANALOG_INPUT1); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 413afc5c5..2207e7bda 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -275,38 +275,45 @@ impl_pin!(P1_13, 1, 13); impl_pin!(P1_14, 1, 14); impl_pin!(P1_15, 1, 15); -impl_ppi_channel!(PPI_CH0, 0 => configurable); -impl_ppi_channel!(PPI_CH1, 1 => configurable); -impl_ppi_channel!(PPI_CH2, 2 => configurable); -impl_ppi_channel!(PPI_CH3, 3 => configurable); -impl_ppi_channel!(PPI_CH4, 4 => configurable); -impl_ppi_channel!(PPI_CH5, 5 => configurable); -impl_ppi_channel!(PPI_CH6, 6 => configurable); -impl_ppi_channel!(PPI_CH7, 7 => configurable); -impl_ppi_channel!(PPI_CH8, 8 => configurable); -impl_ppi_channel!(PPI_CH9, 9 => configurable); -impl_ppi_channel!(PPI_CH10, 10 => configurable); -impl_ppi_channel!(PPI_CH11, 11 => configurable); -impl_ppi_channel!(PPI_CH12, 12 => configurable); -impl_ppi_channel!(PPI_CH13, 13 => configurable); -impl_ppi_channel!(PPI_CH14, 14 => configurable); -impl_ppi_channel!(PPI_CH15, 15 => configurable); -impl_ppi_channel!(PPI_CH16, 16 => configurable); -impl_ppi_channel!(PPI_CH17, 17 => configurable); -impl_ppi_channel!(PPI_CH18, 18 => configurable); -impl_ppi_channel!(PPI_CH19, 19 => configurable); -impl_ppi_channel!(PPI_CH20, 20 => configurable); -impl_ppi_channel!(PPI_CH21, 21 => configurable); -impl_ppi_channel!(PPI_CH22, 22 => configurable); -impl_ppi_channel!(PPI_CH23, 23 => configurable); -impl_ppi_channel!(PPI_CH24, 24 => configurable); -impl_ppi_channel!(PPI_CH25, 25 => configurable); -impl_ppi_channel!(PPI_CH26, 26 => configurable); -impl_ppi_channel!(PPI_CH27, 27 => configurable); -impl_ppi_channel!(PPI_CH28, 28 => configurable); -impl_ppi_channel!(PPI_CH29, 29 => configurable); -impl_ppi_channel!(PPI_CH30, 30 => configurable); -impl_ppi_channel!(PPI_CH31, 31 => configurable); +impl_ppi_channel!(PPI_CH0, DPPIC, 0 => configurable); +impl_ppi_channel!(PPI_CH1, DPPIC, 1 => configurable); +impl_ppi_channel!(PPI_CH2, DPPIC, 2 => configurable); +impl_ppi_channel!(PPI_CH3, DPPIC, 3 => configurable); +impl_ppi_channel!(PPI_CH4, DPPIC, 4 => configurable); +impl_ppi_channel!(PPI_CH5, DPPIC, 5 => configurable); +impl_ppi_channel!(PPI_CH6, DPPIC, 6 => configurable); +impl_ppi_channel!(PPI_CH7, DPPIC, 7 => configurable); +impl_ppi_channel!(PPI_CH8, DPPIC, 8 => configurable); +impl_ppi_channel!(PPI_CH9, DPPIC, 9 => configurable); +impl_ppi_channel!(PPI_CH10, DPPIC, 10 => configurable); +impl_ppi_channel!(PPI_CH11, DPPIC, 11 => configurable); +impl_ppi_channel!(PPI_CH12, DPPIC, 12 => configurable); +impl_ppi_channel!(PPI_CH13, DPPIC, 13 => configurable); +impl_ppi_channel!(PPI_CH14, DPPIC, 14 => configurable); +impl_ppi_channel!(PPI_CH15, DPPIC, 15 => configurable); +impl_ppi_channel!(PPI_CH16, DPPIC, 16 => configurable); +impl_ppi_channel!(PPI_CH17, DPPIC, 17 => configurable); +impl_ppi_channel!(PPI_CH18, DPPIC, 18 => configurable); +impl_ppi_channel!(PPI_CH19, DPPIC, 19 => configurable); +impl_ppi_channel!(PPI_CH20, DPPIC, 20 => configurable); +impl_ppi_channel!(PPI_CH21, DPPIC, 21 => configurable); +impl_ppi_channel!(PPI_CH22, DPPIC, 22 => configurable); +impl_ppi_channel!(PPI_CH23, DPPIC, 23 => configurable); +impl_ppi_channel!(PPI_CH24, DPPIC, 24 => configurable); +impl_ppi_channel!(PPI_CH25, DPPIC, 25 => configurable); +impl_ppi_channel!(PPI_CH26, DPPIC, 26 => configurable); +impl_ppi_channel!(PPI_CH27, DPPIC, 27 => configurable); +impl_ppi_channel!(PPI_CH28, DPPIC, 28 => configurable); +impl_ppi_channel!(PPI_CH29, DPPIC, 29 => configurable); +impl_ppi_channel!(PPI_CH30, DPPIC, 30 => configurable); +impl_ppi_channel!(PPI_CH31, DPPIC, 31 => configurable); + +impl_ppi_group!(PPI_GROUP0, DPPIC, 0); +impl_ppi_group!(PPI_GROUP1, DPPIC, 1); +impl_ppi_group!(PPI_GROUP2, DPPIC, 2); +impl_ppi_group!(PPI_GROUP3, DPPIC, 3); +impl_ppi_group!(PPI_GROUP4, DPPIC, 4); +impl_ppi_group!(PPI_GROUP5, DPPIC, 5); impl_radio!(RADIO, RADIO, RADIO); diff --git a/embassy-nrf/src/chips/nrf54l15_app.rs b/embassy-nrf/src/chips/nrf54l15_app.rs index 901c5e7fc..2e1ac9be8 100644 --- a/embassy-nrf/src/chips/nrf54l15_app.rs +++ b/embassy-nrf/src/chips/nrf54l15_app.rs @@ -200,13 +200,67 @@ pub mod pac { /// The maximum buffer size that the EasyDMA can send/recv in one operation. pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; -//pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; +pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; // 1.5 MB NVM #[allow(unused)] pub const FLASH_SIZE: usize = 1536 * 1024; embassy_hal_internal::peripherals! { + // PPI + PPI00_CH0, + PPI00_CH1, + PPI00_CH2, + PPI00_CH3, + PPI00_CH4, + PPI00_CH5, + PPI00_CH6, + PPI00_CH7, + + PPI20_CH0, + PPI20_CH1, + PPI20_CH2, + PPI20_CH3, + PPI20_CH4, + PPI20_CH5, + PPI20_CH6, + PPI20_CH7, + PPI20_CH8, + PPI20_CH9, + PPI20_CH10, + PPI20_CH11, + PPI20_CH12, + PPI20_CH13, + PPI20_CH14, + PPI20_CH15, + + PPI30_CH0, + PPI30_CH1, + PPI30_CH2, + PPI30_CH3, + + PPI00_GROUP0, + PPI00_GROUP1, + + PPI20_GROUP0, + PPI20_GROUP1, + PPI20_GROUP2, + PPI20_GROUP3, + PPI20_GROUP4, + PPI20_GROUP5, + + PPI30_GROUP0, + PPI30_GROUP1, + + // Timers + TIMER00, + TIMER10, + TIMER20, + TIMER21, + TIMER22, + TIMER23, + TIMER24, + // GPIO port 0 P0_00, P0_01, @@ -253,6 +307,11 @@ embassy_hal_internal::peripherals! { RTC10, RTC30, + // PWM + PWM20, + PWM21, + PWM22, + // SERIAL SERIAL00, SERIAL20, @@ -266,11 +325,6 @@ embassy_hal_internal::peripherals! { // RADIO RADIO, - // TIMER - TIMER00, - TIMER10, - TIMER20, - // PPI BRIDGE PPIB00, PPIB01, @@ -281,10 +335,24 @@ embassy_hal_internal::peripherals! { PPIB22, PPIB30, - // GPIOTE + // GPIOTE instances GPIOTE20, GPIOTE30, + // GPIOTE channels + GPIOTE_CH0, + GPIOTE_CH1, + GPIOTE_CH2, + GPIOTE_CH3, + GPIOTE_CH4, + GPIOTE_CH5, + GPIOTE_CH6, + GPIOTE_CH7, + GPIOTE_CH8, + GPIOTE_CH9, + GPIOTE_CH10, + GPIOTE_CH11, + // CRACEN CRACEN, @@ -311,6 +379,13 @@ impl_pin!(P0_03, 0, 3); impl_pin!(P0_04, 0, 4); impl_pin!(P0_05, 0, 5); impl_pin!(P0_06, 0, 6); +impl_gpiote_pin!(P0_00, GPIOTE30); +impl_gpiote_pin!(P0_01, GPIOTE30); +impl_gpiote_pin!(P0_02, GPIOTE30); +impl_gpiote_pin!(P0_03, GPIOTE30); +impl_gpiote_pin!(P0_04, GPIOTE30); +impl_gpiote_pin!(P0_05, GPIOTE30); +impl_gpiote_pin!(P0_06, GPIOTE30); impl_pin!(P1_00, 1, 0); impl_pin!(P1_01, 1, 1); @@ -330,6 +405,24 @@ impl_pin!(P1_14, 1, 14); impl_pin!(P1_15, 1, 15); impl_pin!(P1_16, 1, 16); +impl_gpiote_pin!(P1_00, GPIOTE20); +impl_gpiote_pin!(P1_01, GPIOTE20); +impl_gpiote_pin!(P1_02, GPIOTE20); +impl_gpiote_pin!(P1_03, GPIOTE20); +impl_gpiote_pin!(P1_04, GPIOTE20); +impl_gpiote_pin!(P1_05, GPIOTE20); +impl_gpiote_pin!(P1_06, GPIOTE20); +impl_gpiote_pin!(P1_07, GPIOTE20); +impl_gpiote_pin!(P1_08, GPIOTE20); +impl_gpiote_pin!(P1_09, GPIOTE20); +impl_gpiote_pin!(P1_10, GPIOTE20); +impl_gpiote_pin!(P1_11, GPIOTE20); +impl_gpiote_pin!(P1_12, GPIOTE20); +impl_gpiote_pin!(P1_13, GPIOTE20); +impl_gpiote_pin!(P1_14, GPIOTE20); +impl_gpiote_pin!(P1_15, GPIOTE20); +impl_gpiote_pin!(P1_16, GPIOTE20); + impl_pin!(P2_00, 2, 0); impl_pin!(P2_01, 2, 1); impl_pin!(P2_02, 2, 2); @@ -351,6 +444,107 @@ impl_wdt!(WDT, WDT31, WDT31, 0); impl_wdt!(WDT0, WDT31, WDT31, 0); #[cfg(feature = "_s")] impl_wdt!(WDT1, WDT30, WDT30, 1); +// DPPI00 channels +impl_ppi_channel!(PPI00_CH0, DPPIC00, 0 => configurable); +impl_ppi_channel!(PPI00_CH1, DPPIC00, 1 => configurable); +impl_ppi_channel!(PPI00_CH2, DPPIC00, 2 => configurable); +impl_ppi_channel!(PPI00_CH3, DPPIC00, 3 => configurable); +impl_ppi_channel!(PPI00_CH4, DPPIC00, 4 => configurable); +impl_ppi_channel!(PPI00_CH5, DPPIC00, 5 => configurable); +impl_ppi_channel!(PPI00_CH6, DPPIC00, 6 => configurable); +impl_ppi_channel!(PPI00_CH7, DPPIC00, 7 => configurable); + +// DPPI20 channels +impl_ppi_channel!(PPI20_CH0, DPPIC20, 0 => configurable); +impl_ppi_channel!(PPI20_CH1, DPPIC20, 1 => configurable); +impl_ppi_channel!(PPI20_CH2, DPPIC20, 2 => configurable); +impl_ppi_channel!(PPI20_CH3, DPPIC20, 3 => configurable); +impl_ppi_channel!(PPI20_CH4, DPPIC20, 4 => configurable); +impl_ppi_channel!(PPI20_CH5, DPPIC20, 5 => configurable); +impl_ppi_channel!(PPI20_CH6, DPPIC20, 6 => configurable); +impl_ppi_channel!(PPI20_CH7, DPPIC20, 7 => configurable); +impl_ppi_channel!(PPI20_CH8, DPPIC20, 8 => configurable); +impl_ppi_channel!(PPI20_CH9, DPPIC20, 9 => configurable); +impl_ppi_channel!(PPI20_CH10, DPPIC20, 10 => configurable); +impl_ppi_channel!(PPI20_CH11, DPPIC20, 11 => configurable); +impl_ppi_channel!(PPI20_CH12, DPPIC20, 12 => configurable); +impl_ppi_channel!(PPI20_CH13, DPPIC20, 13 => configurable); +impl_ppi_channel!(PPI20_CH14, DPPIC20, 14 => configurable); +impl_ppi_channel!(PPI20_CH15, DPPIC20, 15 => configurable); + +// DPPI30 channels +impl_ppi_channel!(PPI30_CH0, DPPIC30, 0 => configurable); +impl_ppi_channel!(PPI30_CH1, DPPIC30, 1 => configurable); +impl_ppi_channel!(PPI30_CH2, DPPIC30, 2 => configurable); +impl_ppi_channel!(PPI30_CH3, DPPIC30, 3 => configurable); + +// DPPI00 groups +impl_ppi_group!(PPI00_GROUP0, DPPIC00, 0); +impl_ppi_group!(PPI00_GROUP1, DPPIC00, 1); + +// DPPI20 groups +impl_ppi_group!(PPI20_GROUP0, DPPIC20, 0); +impl_ppi_group!(PPI20_GROUP1, DPPIC20, 1); +impl_ppi_group!(PPI20_GROUP2, DPPIC20, 2); +impl_ppi_group!(PPI20_GROUP3, DPPIC20, 3); +impl_ppi_group!(PPI20_GROUP4, DPPIC20, 4); +impl_ppi_group!(PPI20_GROUP5, DPPIC20, 5); + +// DPPI30 groups +impl_ppi_group!(PPI30_GROUP0, DPPIC30, 0); +impl_ppi_group!(PPI30_GROUP1, DPPIC30, 1); + +// impl_ppi_channel!(PPI10_CH0, pac::DPPIC10, 0 => static); +// impl_ppi_group!(PPI10_GROUP0, pac::DPPIC10, 0); + +impl_timer!(TIMER00, TIMER00, TIMER00); +impl_timer!(TIMER10, TIMER10, TIMER10); +impl_timer!(TIMER20, TIMER20, TIMER20); +impl_timer!(TIMER21, TIMER21, TIMER21); +impl_timer!(TIMER22, TIMER22, TIMER22); +impl_timer!(TIMER23, TIMER23, TIMER23); +impl_timer!(TIMER24, TIMER24, TIMER24); + +impl_twim!(SERIAL20, TWIM20, SERIAL20); +impl_twim!(SERIAL21, TWIM21, SERIAL21); +impl_twim!(SERIAL22, TWIM22, SERIAL22); +impl_twim!(SERIAL30, TWIM30, SERIAL30); + +impl_twis!(SERIAL20, TWIS20, SERIAL20); +impl_twis!(SERIAL21, TWIS21, SERIAL21); +impl_twis!(SERIAL22, TWIS22, SERIAL22); +impl_twis!(SERIAL30, TWIS30, SERIAL30); + +impl_pwm!(PWM20, PWM20, PWM20); +impl_pwm!(PWM21, PWM21, PWM21); +impl_pwm!(PWM22, PWM22, PWM22); + +impl_spim!(SERIAL00, SPIM00, SERIAL00, 128_000_000); +impl_spim!(SERIAL20, SPIM20, SERIAL20, 16_000_000); +impl_spim!(SERIAL21, SPIM21, SERIAL21, 16_000_000); +impl_spim!(SERIAL22, SPIM22, SERIAL22, 16_000_000); +impl_spim!(SERIAL30, SPIM30, SERIAL30, 16_000_000); + +impl_spis!(SERIAL20, SPIS20, SERIAL20); +impl_spis!(SERIAL21, SPIS21, SERIAL21); +impl_spis!(SERIAL22, SPIS22, SERIAL22); +impl_spis!(SERIAL30, SPIS30, SERIAL30); + +impl_uarte!(SERIAL00, UARTE00, SERIAL00); +impl_uarte!(SERIAL20, UARTE20, SERIAL20); +impl_uarte!(SERIAL21, UARTE21, SERIAL21); +impl_uarte!(SERIAL22, UARTE22, SERIAL22); +impl_uarte!(SERIAL30, UARTE30, SERIAL30); + +// NB: SAADC uses "pin" abstraction, not "AIN" +impl_saadc_input!(P1_04, 1, 4); +impl_saadc_input!(P1_05, 1, 5); +impl_saadc_input!(P1_06, 1, 6); +impl_saadc_input!(P1_07, 1, 7); +impl_saadc_input!(P1_11, 1, 11); +impl_saadc_input!(P1_12, 1, 12); +impl_saadc_input!(P1_13, 1, 13); +impl_saadc_input!(P1_14, 1, 14); embassy_hal_internal::interrupt_mod!( SWI00, diff --git a/embassy-nrf/src/chips/nrf9120.rs b/embassy-nrf/src/chips/nrf9120.rs index 5aee19d97..e9f313fef 100644 --- a/embassy-nrf/src/chips/nrf9120.rs +++ b/embassy-nrf/src/chips/nrf9120.rs @@ -314,22 +314,29 @@ impl_pin!(P0_29, 0, 29); impl_pin!(P0_30, 0, 30); impl_pin!(P0_31, 0, 31); -impl_ppi_channel!(PPI_CH0, 0 => configurable); -impl_ppi_channel!(PPI_CH1, 1 => configurable); -impl_ppi_channel!(PPI_CH2, 2 => configurable); -impl_ppi_channel!(PPI_CH3, 3 => configurable); -impl_ppi_channel!(PPI_CH4, 4 => configurable); -impl_ppi_channel!(PPI_CH5, 5 => configurable); -impl_ppi_channel!(PPI_CH6, 6 => configurable); -impl_ppi_channel!(PPI_CH7, 7 => configurable); -impl_ppi_channel!(PPI_CH8, 8 => configurable); -impl_ppi_channel!(PPI_CH9, 9 => configurable); -impl_ppi_channel!(PPI_CH10, 10 => configurable); -impl_ppi_channel!(PPI_CH11, 11 => configurable); -impl_ppi_channel!(PPI_CH12, 12 => configurable); -impl_ppi_channel!(PPI_CH13, 13 => configurable); -impl_ppi_channel!(PPI_CH14, 14 => configurable); -impl_ppi_channel!(PPI_CH15, 15 => configurable); +impl_ppi_channel!(PPI_CH0, DPPIC, 0 => configurable); +impl_ppi_channel!(PPI_CH1, DPPIC, 1 => configurable); +impl_ppi_channel!(PPI_CH2, DPPIC, 2 => configurable); +impl_ppi_channel!(PPI_CH3, DPPIC, 3 => configurable); +impl_ppi_channel!(PPI_CH4, DPPIC, 4 => configurable); +impl_ppi_channel!(PPI_CH5, DPPIC, 5 => configurable); +impl_ppi_channel!(PPI_CH6, DPPIC, 6 => configurable); +impl_ppi_channel!(PPI_CH7, DPPIC, 7 => configurable); +impl_ppi_channel!(PPI_CH8, DPPIC, 8 => configurable); +impl_ppi_channel!(PPI_CH9, DPPIC, 9 => configurable); +impl_ppi_channel!(PPI_CH10, DPPIC, 10 => configurable); +impl_ppi_channel!(PPI_CH11, DPPIC, 11 => configurable); +impl_ppi_channel!(PPI_CH12, DPPIC, 12 => configurable); +impl_ppi_channel!(PPI_CH13, DPPIC, 13 => configurable); +impl_ppi_channel!(PPI_CH14, DPPIC, 14 => configurable); +impl_ppi_channel!(PPI_CH15, DPPIC, 15 => configurable); + +impl_ppi_group!(PPI_GROUP0, DPPIC, 0); +impl_ppi_group!(PPI_GROUP1, DPPIC, 1); +impl_ppi_group!(PPI_GROUP2, DPPIC, 2); +impl_ppi_group!(PPI_GROUP3, DPPIC, 3); +impl_ppi_group!(PPI_GROUP4, DPPIC, 4); +impl_ppi_group!(PPI_GROUP5, DPPIC, 5); impl_saadc_input!(P0_13, ANALOG_INPUT0); impl_saadc_input!(P0_14, ANALOG_INPUT1); diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 64aec217c..4c6f055dd 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -314,22 +314,29 @@ impl_pin!(P0_29, 0, 29); impl_pin!(P0_30, 0, 30); impl_pin!(P0_31, 0, 31); -impl_ppi_channel!(PPI_CH0, 0 => configurable); -impl_ppi_channel!(PPI_CH1, 1 => configurable); -impl_ppi_channel!(PPI_CH2, 2 => configurable); -impl_ppi_channel!(PPI_CH3, 3 => configurable); -impl_ppi_channel!(PPI_CH4, 4 => configurable); -impl_ppi_channel!(PPI_CH5, 5 => configurable); -impl_ppi_channel!(PPI_CH6, 6 => configurable); -impl_ppi_channel!(PPI_CH7, 7 => configurable); -impl_ppi_channel!(PPI_CH8, 8 => configurable); -impl_ppi_channel!(PPI_CH9, 9 => configurable); -impl_ppi_channel!(PPI_CH10, 10 => configurable); -impl_ppi_channel!(PPI_CH11, 11 => configurable); -impl_ppi_channel!(PPI_CH12, 12 => configurable); -impl_ppi_channel!(PPI_CH13, 13 => configurable); -impl_ppi_channel!(PPI_CH14, 14 => configurable); -impl_ppi_channel!(PPI_CH15, 15 => configurable); +impl_ppi_channel!(PPI_CH0, DPPIC, 0 => configurable); +impl_ppi_channel!(PPI_CH1, DPPIC, 1 => configurable); +impl_ppi_channel!(PPI_CH2, DPPIC, 2 => configurable); +impl_ppi_channel!(PPI_CH3, DPPIC, 3 => configurable); +impl_ppi_channel!(PPI_CH4, DPPIC, 4 => configurable); +impl_ppi_channel!(PPI_CH5, DPPIC, 5 => configurable); +impl_ppi_channel!(PPI_CH6, DPPIC, 6 => configurable); +impl_ppi_channel!(PPI_CH7, DPPIC, 7 => configurable); +impl_ppi_channel!(PPI_CH8, DPPIC, 8 => configurable); +impl_ppi_channel!(PPI_CH9, DPPIC, 9 => configurable); +impl_ppi_channel!(PPI_CH10, DPPIC, 10 => configurable); +impl_ppi_channel!(PPI_CH11, DPPIC, 11 => configurable); +impl_ppi_channel!(PPI_CH12, DPPIC, 12 => configurable); +impl_ppi_channel!(PPI_CH13, DPPIC, 13 => configurable); +impl_ppi_channel!(PPI_CH14, DPPIC, 14 => configurable); +impl_ppi_channel!(PPI_CH15, DPPIC, 15 => configurable); + +impl_ppi_group!(PPI_GROUP0, DPPIC, 0); +impl_ppi_group!(PPI_GROUP1, DPPIC, 1); +impl_ppi_group!(PPI_GROUP2, DPPIC, 2); +impl_ppi_group!(PPI_GROUP3, DPPIC, 3); +impl_ppi_group!(PPI_GROUP4, DPPIC, 4); +impl_ppi_group!(PPI_GROUP5, DPPIC, 5); impl_saadc_input!(P0_13, ANALOG_INPUT0); impl_saadc_input!(P0_14, ANALOG_INPUT1); diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 7ed3a7927..43d1b9cb2 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -585,7 +585,6 @@ impl SealedPin for AnyPin { // ==================== #[cfg(not(feature = "_nrf51"))] -#[cfg_attr(feature = "_nrf54l", allow(unused))] // TODO pub(crate) trait PselBits { fn psel_bits(&self) -> pac::shared::regs::Psel; } @@ -602,7 +601,6 @@ impl<'a, P: Pin> PselBits for Option> { } #[cfg(not(feature = "_nrf51"))] -#[cfg_attr(feature = "_nrf54l", allow(unused))] // TODO pub(crate) const DISCONNECTED: Psel = Psel(1 << 31); #[cfg(not(feature = "_nrf51"))] diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 3658657c0..ed95f5d83 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -1,4 +1,5 @@ //! GPIO task/event (GPIOTE) driver. +#![macro_use] use core::convert::Infallible; use core::future::{Future, poll_fn}; @@ -7,7 +8,7 @@ use core::task::{Context, Poll}; use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; use embassy_sync::waitqueue::AtomicWaker; -use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin, SealedPin as _}; +use crate::gpio::{AnyPin, Flex, Input, Level, Output, OutputDrive, Pin as GpioPin, Pull, SealedPin as _}; use crate::interrupt::InterruptExt; #[cfg(not(feature = "_nrf51"))] use crate::pac::gpio::vals::Detectmode; @@ -19,13 +20,28 @@ use crate::{interrupt, pac, peripherals}; #[cfg(feature = "_nrf51")] /// Amount of GPIOTE channels in the chip. const CHANNEL_COUNT: usize = 4; -#[cfg(not(feature = "_nrf51"))] +#[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))] /// Amount of GPIOTE channels in the chip. const CHANNEL_COUNT: usize = 8; - -#[cfg(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340"))] +#[cfg(any(feature = "_nrf54l"))] +/// Amount of GPIOTE channels in the chip. +const CHANNEL_COUNT: usize = 12; +/// Max channels per port +const CHANNELS_PER_PORT: usize = 8; + +#[cfg(any( + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf5340", + feature = "_nrf54l" +))] const PIN_COUNT: usize = 48; -#[cfg(not(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340")))] +#[cfg(not(any( + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf5340", + feature = "_nrf54l" +)))] const PIN_COUNT: usize = 32; #[allow(clippy::declare_interior_mutable_const)] @@ -54,18 +70,6 @@ pub enum OutputChannelPolarity { Toggle, } -fn regs() -> pac::gpiote::Gpiote { - cfg_if::cfg_if! { - if #[cfg(any(feature="nrf5340-app-s", feature="nrf9160-s", feature="nrf9120-s"))] { - pac::GPIOTE0 - } else if #[cfg(any(feature="nrf5340-app-ns", feature="nrf9160-ns", feature="nrf9120-ns"))] { - pac::GPIOTE1 - } else { - pac::GPIOTE - } - } -} - pub(crate) fn init(irq_prio: crate::interrupt::Priority) { // no latched GPIO detect in nrf51. #[cfg(not(feature = "_nrf51"))] @@ -77,9 +81,9 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) { for &p in ports { // Enable latched detection - #[cfg(feature = "_s")] + #[cfg(all(feature = "_s", not(feature = "_nrf54l")))] p.detectmode_sec().write(|w| w.set_detectmode(Detectmode::LDETECT)); - #[cfg(not(feature = "_s"))] + #[cfg(any(not(feature = "_s"), feature = "_nrf54l"))] p.detectmode().write(|w| w.set_detectmode(Detectmode::LDETECT)); // Clear latch p.latch().write(|w| w.0 = 0xFFFFFFFF) @@ -88,57 +92,136 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) { // Enable interrupts #[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s", feature = "nrf9120-s"))] - let irq = interrupt::GPIOTE0; + let irqs = &[(pac::GPIOTE0, interrupt::GPIOTE0)]; #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns", feature = "nrf9120-ns"))] - let irq = interrupt::GPIOTE1; + let irqs = &[(pac::GPIOTE1, interrupt::GPIOTE1)]; #[cfg(any(feature = "_nrf51", feature = "_nrf52", feature = "nrf5340-net"))] - let irq = interrupt::GPIOTE; + let irqs = &[(pac::GPIOTE, interrupt::GPIOTE)]; + #[cfg(any(feature = "_nrf54l"))] + let irqs = &[ + #[cfg(feature = "_s")] + (pac::GPIOTE20, interrupt::GPIOTE20_0), + #[cfg(feature = "_s")] + (pac::GPIOTE30, interrupt::GPIOTE30_0), + #[cfg(feature = "_ns")] + (pac::GPIOTE20, interrupt::GPIOTE20_1), + #[cfg(feature = "_ns")] + (pac::GPIOTE30, interrupt::GPIOTE30_1), + ]; + + for (inst, irq) in irqs { + irq.unpend(); + irq.set_priority(irq_prio); + unsafe { irq.enable() }; - irq.unpend(); - irq.set_priority(irq_prio); - unsafe { irq.enable() }; + let g = inst; + #[cfg(not(feature = "_nrf54l"))] + g.intenset(INTNUM).write(|w| w.set_port(true)); - let g = regs(); - g.intenset().write(|w| w.set_port(true)); + #[cfg(all(feature = "_nrf54l", feature = "_ns"))] + g.intenset(INTNUM).write(|w| w.set_port0nonsecure(true)); + + #[cfg(all(feature = "_nrf54l", feature = "_s"))] + g.intenset(INTNUM).write(|w| w.set_port0secure(true)); + } } +#[cfg(all(feature = "_nrf54l", feature = "_ns"))] +const INTNUM: usize = 1; + +#[cfg(any(not(feature = "_nrf54l"), feature = "_s"))] +const INTNUM: usize = 0; + #[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s", feature = "nrf9120-s"))] #[cfg(feature = "rt")] #[interrupt] fn GPIOTE0() { - unsafe { handle_gpiote_interrupt() }; + unsafe { handle_gpiote_interrupt(pac::GPIOTE0) }; } #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns", feature = "nrf9120-ns"))] #[cfg(feature = "rt")] #[interrupt] fn GPIOTE1() { - unsafe { handle_gpiote_interrupt() }; + unsafe { handle_gpiote_interrupt(pac::GPIOTE1) }; } #[cfg(any(feature = "_nrf51", feature = "_nrf52", feature = "nrf5340-net"))] #[cfg(feature = "rt")] #[interrupt] fn GPIOTE() { - unsafe { handle_gpiote_interrupt() }; + info!("GPIOTE!"); + unsafe { handle_gpiote_interrupt(pac::GPIOTE) }; } -unsafe fn handle_gpiote_interrupt() { - let g = regs(); +#[cfg(all(feature = "_nrf54l", feature = "_s"))] +#[cfg(feature = "rt")] +#[interrupt] +fn GPIOTE20_0() { + info!("GPIOTE20_0!"); + unsafe { handle_gpiote_interrupt(pac::GPIOTE20) }; +} - for i in 0..CHANNEL_COUNT { +#[cfg(all(feature = "_nrf54l", feature = "_s"))] +#[cfg(feature = "rt")] +#[interrupt] +fn GPIOTE30_0() { + info!("GPIOTE30_0!"); + unsafe { handle_gpiote_interrupt(pac::GPIOTE30) }; +} + +#[cfg(all(feature = "_nrf54l", feature = "_ns"))] +#[cfg(feature = "rt")] +#[interrupt] +fn GPIOTE20_1() { + info!("GPIOTE20_1!"); + unsafe { handle_gpiote_interrupt(pac::GPIOTE20) }; +} + +#[cfg(all(feature = "_nrf54l", feature = "_ns"))] +#[cfg(feature = "rt")] +#[interrupt] +fn GPIOTE30_1() { + info!("GPIOTE30_1!"); + unsafe { handle_gpiote_interrupt(pac::GPIOTE30) }; +} + +unsafe fn handle_gpiote_interrupt(g: pac::gpiote::Gpiote) { + for c in 0..CHANNEL_COUNT { + let i = c % CHANNELS_PER_PORT; if g.events_in(i).read() != 0 { - g.intenclr().write(|w| w.0 = 1 << i); - CHANNEL_WAKERS[i].wake(); + info!("Clear IRQ {} waker {}", INTNUM, c); + g.intenclr(INTNUM).write(|w| w.0 = 1 << i); + CHANNEL_WAKERS[c].wake(); } } - if g.events_port().read() != 0 { - g.events_port().write_value(0); + #[cfg(not(feature = "_nrf54l"))] + let eport = g.events_port(0); - #[cfg(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340"))] + #[cfg(all(feature = "_nrf54l", feature = "_ns"))] + let eport = g.events_port(0).nonsecure(); + + #[cfg(all(feature = "_nrf54l", feature = "_s"))] + let eport = g.events_port(0).secure(); + + if eport.read() != 0 { + eport.write_value(0); + + #[cfg(any( + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf5340", + feature = "_nrf54l" + ))] let ports = &[pac::P0, pac::P1]; - #[cfg(not(any(feature = "_nrf51", feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340")))] + #[cfg(not(any( + feature = "_nrf51", + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf5340", + feature = "_nrf54l" + )))] let ports = &[pac::P0]; #[cfg(feature = "_nrf51")] let ports = &[pac::GPIO]; @@ -162,9 +245,14 @@ unsafe fn handle_gpiote_interrupt() { #[cfg(not(feature = "_nrf51"))] for (port, &p) in ports.iter().enumerate() { + info!("Interrupt port {}", port); let bits = p.latch().read().0; for pin in BitIter(bits) { p.pin_cnf(pin as usize).modify(|w| w.set_sense(Sense::DISABLED)); + + let w = port * 32 + pin as usize; + + info!("Interrupt pin {}, waker {}", pin as usize, w); PORT_WAKERS[port * 32 + pin as usize].wake(); } p.latch().write(|w| w.0 = bits); @@ -207,19 +295,43 @@ impl InputChannel<'static> { impl<'d> Drop for InputChannel<'d> { fn drop(&mut self) { - let g = regs(); + let g = self.ch.regs(); let num = self.ch.number(); g.config(num).write(|w| w.set_mode(Mode::DISABLED)); - g.intenclr().write(|w| w.0 = 1 << num); + g.intenclr(INTNUM).write(|w| w.0 = 1 << num); } } impl<'d> InputChannel<'d> { /// Create a new GPIOTE input channel driver. - pub fn new(ch: Peri<'d, impl Channel>, pin: Input<'d>, polarity: InputChannelPolarity) -> Self { - let g = regs(); - let num = ch.number(); + #[cfg(feature = "_nrf54l")] + pub fn new>( + ch: Peri<'d, C>, + pin: Peri<'d, T>, + pull: Pull, + polarity: InputChannelPolarity, + ) -> Self { + let pin = Input::new(pin, pull); + let ch = ch.into(); + Self::new_inner(ch, pin, polarity) + } + /// Create a new GPIOTE output channel driver. + #[cfg(not(feature = "_nrf54l"))] + pub fn new( + ch: Peri<'d, C>, + pin: Peri<'d, T>, + pull: Pull, + polarity: InputChannelPolarity, + ) -> Self { + let pin = Input::new(pin, pull); + let ch = ch.into(); + Self::new_inner(ch, pin, polarity) + } + + fn new_inner(ch: Peri<'d, AnyChannel>, pin: Input<'d>, polarity: InputChannelPolarity) -> Self { + let g = ch.regs(); + let num = ch.number(); g.config(num).write(|w| { w.set_mode(Mode::EVENT); match polarity { @@ -228,30 +340,38 @@ impl<'d> InputChannel<'d> { InputChannelPolarity::None => w.set_polarity(Polarity::NONE), InputChannelPolarity::Toggle => w.set_polarity(Polarity::TOGGLE), }; - #[cfg(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340"))] + #[cfg(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340",))] w.set_port(match pin.pin.pin.port() { crate::gpio::Port::Port0 => false, crate::gpio::Port::Port1 => true, }); + #[cfg(any(feature = "_nrf54l"))] + w.set_port(match pin.pin.pin.port() { + crate::gpio::Port::Port0 => 0, + crate::gpio::Port::Port1 => 1, + crate::gpio::Port::Port2 => 2, + }); w.set_psel(pin.pin.pin.pin()); }); g.events_in(num).write_value(0); - InputChannel { ch: ch.into(), pin } + InputChannel { ch, pin } } /// Asynchronously wait for an event in this channel. pub async fn wait(&self) { - let g = regs(); + let g = self.ch.regs(); let num = self.ch.number(); + let waker = self.ch.waker(); // Enable interrupt g.events_in(num).write_value(0); - g.intenset().write(|w| w.0 = 1 << num); + g.intenset(INTNUM).write(|w| w.0 = 1 << num); poll_fn(|cx| { - CHANNEL_WAKERS[num].register(cx.waker()); + info!("Waiting for channel waker {}", num); + CHANNEL_WAKERS[waker].register(cx.waker()); if g.events_in(num).read() != 0 { Poll::Ready(()) @@ -269,7 +389,7 @@ impl<'d> InputChannel<'d> { /// Returns the IN event, for use with PPI. pub fn event_in(&self) -> Event<'d> { - let g = regs(); + let g = self.ch.regs(); Event::from_reg(g.events_in(self.ch.number())) } } @@ -291,17 +411,44 @@ impl OutputChannel<'static> { impl<'d> Drop for OutputChannel<'d> { fn drop(&mut self) { - let g = regs(); + let g = self.ch.regs(); let num = self.ch.number(); g.config(num).write(|w| w.set_mode(Mode::DISABLED)); - g.intenclr().write(|w| w.0 = 1 << num); + g.intenclr(INTNUM).write(|w| w.0 = 1 << num); } } impl<'d> OutputChannel<'d> { /// Create a new GPIOTE output channel driver. - pub fn new(ch: Peri<'d, impl Channel>, pin: Output<'d>, polarity: OutputChannelPolarity) -> Self { - let g = regs(); + #[cfg(feature = "_nrf54l")] + pub fn new>( + ch: Peri<'d, C>, + pin: Peri<'d, T>, + initial_output: Level, + drive: OutputDrive, + polarity: OutputChannelPolarity, + ) -> Self { + let pin = Output::new(pin, initial_output, drive); + let ch = ch.into(); + Self::new_inner(ch, pin, polarity) + } + + /// Create a new GPIOTE output channel driver. + #[cfg(not(feature = "_nrf54l"))] + pub fn new( + ch: Peri<'d, C>, + pin: Peri<'d, T>, + initial_output: Level, + drive: OutputDrive, + polarity: OutputChannelPolarity, + ) -> Self { + let pin = Output::new(pin, initial_output, drive); + let ch = ch.into(); + Self::new_inner(ch, pin, polarity) + } + + fn new_inner(ch: Peri<'d, AnyChannel>, pin: Output<'d>, polarity: OutputChannelPolarity) -> Self { + let g = ch.regs(); let num = ch.number(); g.config(num).write(|w| { @@ -320,52 +467,55 @@ impl<'d> OutputChannel<'d> { crate::gpio::Port::Port0 => false, crate::gpio::Port::Port1 => true, }); + #[cfg(any(feature = "_nrf54l"))] + w.set_port(match pin.pin.pin.port() { + crate::gpio::Port::Port0 => 0, + crate::gpio::Port::Port1 => 1, + crate::gpio::Port::Port2 => 2, + }); w.set_psel(pin.pin.pin.pin()); }); - OutputChannel { - ch: ch.into(), - _pin: pin, - } + OutputChannel { ch, _pin: pin } } /// Triggers the OUT task (does the action as configured with task_out_polarity, defaults to Toggle). pub fn out(&self) { - let g = regs(); + let g = self.ch.regs(); g.tasks_out(self.ch.number()).write_value(1); } /// Triggers the SET task (set associated pin high). #[cfg(not(feature = "_nrf51"))] pub fn set(&self) { - let g = regs(); + let g = self.ch.regs(); g.tasks_set(self.ch.number()).write_value(1); } /// Triggers the CLEAR task (set associated pin low). #[cfg(not(feature = "_nrf51"))] pub fn clear(&self) { - let g = regs(); + let g = self.ch.regs(); g.tasks_clr(self.ch.number()).write_value(1); } /// Returns the OUT task, for use with PPI. pub fn task_out(&self) -> Task<'d> { - let g = regs(); + let g = self.ch.regs(); Task::from_reg(g.tasks_out(self.ch.number())) } /// Returns the CLR task, for use with PPI. #[cfg(not(feature = "_nrf51"))] pub fn task_clr(&self) -> Task<'d> { - let g = regs(); + let g = self.ch.regs(); Task::from_reg(g.tasks_clr(self.ch.number())) } /// Returns the SET task, for use with PPI. #[cfg(not(feature = "_nrf51"))] pub fn task_set(&self) -> Task<'d> { - let g = regs(); + let g = self.ch.regs(); Task::from_reg(g.tasks_set(self.ch.number())) } } @@ -395,6 +545,7 @@ impl<'a> Future for PortInputFuture<'a> { type Output = (); fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + info!("register waker on {}", self.pin.port() as usize); PORT_WAKERS[self.pin.pin_port() as usize].register(cx.waker()); if self.pin.conf().read().sense() == Sense::DISABLED { @@ -467,31 +618,52 @@ impl<'d> Flex<'d> { PortInputFuture::new(self.pin.reborrow()).await } } - // ======================= +// -trait SealedChannel {} +trait SealedChannel { + fn waker(&self) -> usize; + fn regs(&self) -> pac::gpiote::Gpiote; +} /// GPIOTE channel trait. /// /// Implemented by all GPIOTE channels. #[allow(private_bounds)] pub trait Channel: PeripheralType + SealedChannel + Into + Sized + 'static { + #[cfg(feature = "_nrf54l")] + /// GPIOTE instance this channel belongs to. + type Instance: GpioteInstance; /// Get the channel number. fn number(&self) -> usize; } -/// Type-erased channel. -/// -/// Obtained by calling `Channel::into()`. -/// -/// This allows using several channels in situations that might require -/// them to be the same type, like putting them in an array. -pub struct AnyChannel { +struct AnyChannel { number: u8, + regs: pac::gpiote::Gpiote, + waker: u8, } + impl_peripheral!(AnyChannel); -impl SealedChannel for AnyChannel {} + +impl SealedChannel for AnyChannel { + fn waker(&self) -> usize { + self.waker as usize + } + + fn regs(&self) -> pac::gpiote::Gpiote { + self.regs + } +} + +#[cfg(feature = "_nrf54l")] +impl AnyChannel { + fn number(&self) -> usize { + self.number as usize + } +} + +#[cfg(not(feature = "_nrf54l"))] impl Channel for AnyChannel { fn number(&self) -> usize { self.number as usize @@ -499,9 +671,19 @@ impl Channel for AnyChannel { } macro_rules! impl_channel { - ($type:ident, $number:expr) => { - impl SealedChannel for peripherals::$type {} + ($type:ident, $inst:ident, $number:expr, $waker:expr) => { + impl SealedChannel for peripherals::$type { + fn waker(&self) -> usize { + $waker as usize + } + + fn regs(&self) -> pac::gpiote::Gpiote { + pac::$inst + } + } impl Channel for peripherals::$type { + #[cfg(feature = "_nrf54l")] + type Instance = peripherals::$inst; fn number(&self) -> usize { $number as usize } @@ -511,24 +693,97 @@ macro_rules! impl_channel { fn from(val: peripherals::$type) -> Self { Self { number: val.number() as u8, + waker: val.waker() as u8, + regs: val.regs(), } } } }; } -impl_channel!(GPIOTE_CH0, 0); -impl_channel!(GPIOTE_CH1, 1); -impl_channel!(GPIOTE_CH2, 2); -impl_channel!(GPIOTE_CH3, 3); -#[cfg(not(feature = "_nrf51"))] -impl_channel!(GPIOTE_CH4, 4); -#[cfg(not(feature = "_nrf51"))] -impl_channel!(GPIOTE_CH5, 5); -#[cfg(not(feature = "_nrf51"))] -impl_channel!(GPIOTE_CH6, 6); -#[cfg(not(feature = "_nrf51"))] -impl_channel!(GPIOTE_CH7, 7); +cfg_if::cfg_if! { + if #[cfg(feature = "_nrf54l")] { + trait SealedGpioteInstance {} + /// Represents a GPIOTE instance. + #[allow(private_bounds)] + pub trait GpioteInstance: PeripheralType + SealedGpioteInstance + Sized + 'static {} + + macro_rules! impl_gpiote { + ($type:ident) => { + impl SealedGpioteInstance for peripherals::$type {} + impl GpioteInstance for peripherals::$type {} + }; + } + + pub(crate) trait SealedGpiotePin {} + + /// Represents a GPIO pin that can be used with GPIOTE. + #[allow(private_bounds)] + pub trait GpiotePin: GpioPin + SealedGpiotePin { + /// The GPIOTE instance this pin belongs to. + type Instance: GpioteInstance; + } + + macro_rules! impl_gpiote_pin { + ($type:ident, $inst:ident) => { + #[cfg(feature = "gpiote")] + impl crate::gpiote::SealedGpiotePin for peripherals::$type {} + #[cfg(feature = "gpiote")] + impl crate::gpiote::GpiotePin for peripherals::$type { + type Instance = peripherals::$inst; + } + }; + } + + impl_gpiote!(GPIOTE20); + impl_gpiote!(GPIOTE30); + impl_channel!(GPIOTE_CH0, GPIOTE20, 0, 0); + impl_channel!(GPIOTE_CH1, GPIOTE20, 1, 1); + impl_channel!(GPIOTE_CH2, GPIOTE20, 2, 2); + impl_channel!(GPIOTE_CH3, GPIOTE20, 3, 3); + impl_channel!(GPIOTE_CH4, GPIOTE20, 4, 4); + impl_channel!(GPIOTE_CH5, GPIOTE20, 5, 5); + impl_channel!(GPIOTE_CH6, GPIOTE20, 6, 6); + impl_channel!(GPIOTE_CH7, GPIOTE20, 7, 7); + + impl_channel!(GPIOTE_CH8, GPIOTE30, 0, 8); + impl_channel!(GPIOTE_CH9, GPIOTE30, 1, 9); + impl_channel!(GPIOTE_CH10, GPIOTE30, 2, 10); + impl_channel!(GPIOTE_CH11, GPIOTE30, 3, 11); + } else if #[cfg(feature = "_nrf51")] { + impl_channel!(GPIOTE_CH0, GPIOTE, 0, 0); + impl_channel!(GPIOTE_CH1, GPIOTE, 1, 1); + impl_channel!(GPIOTE_CH2, GPIOTE, 2, 2); + impl_channel!(GPIOTE_CH3, GPIOTE, 3, 3); + } else if #[cfg(all(feature = "_s", any(feature = "_nrf91", feature = "_nrf5340")))] { + impl_channel!(GPIOTE_CH0, GPIOTE0, 0, 0); + impl_channel!(GPIOTE_CH1, GPIOTE0, 1, 1); + impl_channel!(GPIOTE_CH2, GPIOTE0, 2, 2); + impl_channel!(GPIOTE_CH3, GPIOTE0, 3, 3); + impl_channel!(GPIOTE_CH4, GPIOTE0, 4, 4); + impl_channel!(GPIOTE_CH5, GPIOTE0, 5, 5); + impl_channel!(GPIOTE_CH6, GPIOTE0, 6, 6); + impl_channel!(GPIOTE_CH7, GPIOTE0, 7, 7); + } else if #[cfg(all(feature = "_ns", any(feature = "_nrf91", feature = "_nrf5340")))] { + impl_channel!(GPIOTE_CH0, GPIOTE1, 0, 0); + impl_channel!(GPIOTE_CH1, GPIOTE1, 1, 1); + impl_channel!(GPIOTE_CH2, GPIOTE1, 2, 2); + impl_channel!(GPIOTE_CH3, GPIOTE1, 3, 3); + impl_channel!(GPIOTE_CH4, GPIOTE1, 4, 4); + impl_channel!(GPIOTE_CH5, GPIOTE1, 5, 5); + impl_channel!(GPIOTE_CH6, GPIOTE1, 6, 6); + impl_channel!(GPIOTE_CH7, GPIOTE1, 7, 7); + } else { + impl_channel!(GPIOTE_CH0, GPIOTE, 0, 0); + impl_channel!(GPIOTE_CH1, GPIOTE, 1, 1); + impl_channel!(GPIOTE_CH2, GPIOTE, 2, 2); + impl_channel!(GPIOTE_CH3, GPIOTE, 3, 3); + impl_channel!(GPIOTE_CH4, GPIOTE, 4, 4); + impl_channel!(GPIOTE_CH5, GPIOTE, 5, 5); + impl_channel!(GPIOTE_CH6, GPIOTE, 6, 6); + impl_channel!(GPIOTE_CH7, GPIOTE, 7, 7); + } +} // ==================== diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 705c77453..2f7505746 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -76,14 +76,12 @@ pub(crate) mod util; #[cfg(feature = "_time-driver")] mod time_driver; -#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] pub mod buffered_uarte; #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] pub mod egu; pub mod gpio; -#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(feature = "gpiote")] pub mod gpiote; #[cfg(not(feature = "_nrf54l"))] // TODO @@ -119,9 +117,7 @@ pub mod pdm; #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(any(feature = "nrf52840", feature = "nrf9160-s", feature = "nrf9160-ns"))] pub mod power; -#[cfg(not(feature = "_nrf54l"))] // TODO pub mod ppi; -#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(any( feature = "_nrf51", feature = "nrf52805", @@ -156,26 +152,19 @@ pub mod reset; #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] pub mod rng; pub mod rtc; -#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))] pub mod saadc; -#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] pub mod spim; -#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] pub mod spis; #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] pub mod temp; -#[cfg(not(feature = "_nrf54l"))] // TODO pub mod timer; -#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] pub mod twim; -#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] pub mod twis; -#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] pub mod uarte; #[cfg(not(feature = "_nrf54l"))] // TODO @@ -1153,7 +1142,6 @@ pub fn init(config: config::Config) -> Peripherals { } // Init GPIOTE - #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(feature = "gpiote")] gpiote::init(config.gpiote_interrupt_priority); diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index 168647be3..d43a25c4e 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -1,11 +1,12 @@ use super::{Channel, ConfigurableChannel, Event, Ppi, Task}; -use crate::{Peri, pac}; +use crate::Peri; const DPPI_ENABLE_BIT: u32 = 0x8000_0000; const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF; -pub(crate) fn regs() -> pac::dppic::Dppic { - pac::DPPIC +#[cfg(not(feature = "_nrf54l"))] +pub(crate) fn regs() -> crate::pac::dppic::Dppic { + crate::pac::DPPIC } impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { @@ -49,13 +50,13 @@ impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'d, /// Enables the channel. pub fn enable(&mut self) { let n = self.ch.number(); - regs().chenset().write(|w| w.0 = 1 << n); + self.ch.regs().chenset().write(|w| w.0 = 1 << n); } /// Disables the channel. pub fn disable(&mut self) { let n = self.ch.number(); - regs().chenclr().write(|w| w.0 = 1 << n); + self.ch.regs().chenclr().write(|w| w.0 = 1 << n); } } diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index f30c2218d..a880d3188 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -21,11 +21,13 @@ use core::ptr::NonNull; use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; use crate::pac::common::{RW, Reg, W}; -use crate::peripherals; +use crate::pac::{self}; #[cfg_attr(feature = "_dppi", path = "dppi.rs")] #[cfg_attr(feature = "_ppi", path = "ppi.rs")] mod _version; + +#[allow(unused_imports)] pub(crate) use _version::*; /// PPI channel driver. @@ -47,7 +49,7 @@ impl<'d, G: Group> PpiGroup<'d, G> { /// /// The group is initialized as containing no channels. pub fn new(g: Peri<'d, G>) -> Self { - let r = regs(); + let r = g.regs(); let n = g.number(); r.chg(n).write(|_| ()); @@ -61,7 +63,7 @@ impl<'d, G: Group> PpiGroup<'d, G> { &mut self, ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>, ) { - let r = regs(); + let r = self.g.regs(); let ng = self.g.number(); let nc = ch.ch.number(); r.chg(ng).modify(|w| w.set_ch(nc, true)); @@ -74,7 +76,7 @@ impl<'d, G: Group> PpiGroup<'d, G> { &mut self, ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>, ) { - let r = regs(); + let r = self.g.regs(); let ng = self.g.number(); let nc = ch.ch.number(); r.chg(ng).modify(|w| w.set_ch(nc, false)); @@ -83,13 +85,13 @@ impl<'d, G: Group> PpiGroup<'d, G> { /// Enable all the channels in this group. pub fn enable_all(&mut self) { let n = self.g.number(); - regs().tasks_chg(n).en().write_value(1); + self.g.regs().tasks_chg(n).en().write_value(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_value(1); + self.g.regs().tasks_chg(n).dis().write_value(1); } /// Get a reference to the "enable all" task. @@ -97,7 +99,7 @@ impl<'d, G: Group> PpiGroup<'d, G> { /// When triggered, it will enable all the channels in this group. pub fn task_enable_all(&self) -> Task<'d> { let n = self.g.number(); - Task::from_reg(regs().tasks_chg(n).en()) + Task::from_reg(self.g.regs().tasks_chg(n).en()) } /// Get a reference to the "disable all" task. @@ -105,7 +107,7 @@ impl<'d, G: Group> PpiGroup<'d, G> { /// When triggered, it will disable all the channels in this group. pub fn task_disable_all(&self) -> Task<'d> { let n = self.g.number(); - Task::from_reg(regs().tasks_chg(n).dis()) + Task::from_reg(self.g.regs().tasks_chg(n).dis()) } } impl PpiGroup<'static, G> { @@ -119,7 +121,7 @@ impl PpiGroup<'static, G> { impl<'d, G: Group> Drop for PpiGroup<'d, G> { fn drop(&mut self) { - let r = regs(); + let r = self.g.regs(); let n = self.g.number(); r.chg(n).write(|_| ()); } @@ -211,8 +213,16 @@ unsafe impl Send for Event<'_> {} // ====================== // traits -pub(crate) trait SealedChannel {} -pub(crate) trait SealedGroup {} +pub(crate) trait SealedChannel { + #[cfg(feature = "_dppi")] + fn regs(&self) -> pac::dppic::Dppic; +} +pub(crate) trait SealedGroup { + #[cfg(feature = "_dppi")] + fn regs(&self) -> pac::dppic::Dppic; + #[cfg(not(feature = "_dppi"))] + fn regs(&self) -> pac::ppi::Ppi; +} /// Interface for PPI channels. #[allow(private_bounds)] @@ -241,9 +251,16 @@ pub trait Group: SealedGroup + PeripheralType + Into + Sized + 'static /// This can be used to have fewer generic parameters in some places. pub struct AnyStaticChannel { pub(crate) number: u8, + #[cfg(feature = "_dppi")] + pub(crate) regs: pac::dppic::Dppic, } impl_peripheral!(AnyStaticChannel); -impl SealedChannel for AnyStaticChannel {} +impl SealedChannel for AnyStaticChannel { + #[cfg(feature = "_dppi")] + fn regs(&self) -> pac::dppic::Dppic { + self.regs + } +} impl Channel for AnyStaticChannel { fn number(&self) -> usize { self.number as usize @@ -255,9 +272,16 @@ impl StaticChannel for AnyStaticChannel {} /// This can be used to have fewer generic parameters in some places. pub struct AnyConfigurableChannel { pub(crate) number: u8, + #[cfg(feature = "_dppi")] + pub(crate) regs: pac::dppic::Dppic, } impl_peripheral!(AnyConfigurableChannel); -impl SealedChannel for AnyConfigurableChannel {} +impl SealedChannel for AnyConfigurableChannel { + #[cfg(feature = "_dppi")] + fn regs(&self) -> pac::dppic::Dppic { + self.regs + } +} impl Channel for AnyConfigurableChannel { fn number(&self) -> usize { self.number as usize @@ -267,32 +291,41 @@ impl ConfigurableChannel for AnyConfigurableChannel {} #[cfg(not(feature = "_nrf51"))] macro_rules! impl_ppi_channel { - ($type:ident, $number:expr) => { - impl crate::ppi::SealedChannel for peripherals::$type {} + ($type:ident, $inst:ident, $number:expr) => { + impl crate::ppi::SealedChannel for peripherals::$type { + #[cfg(feature = "_dppi")] + fn regs(&self) -> pac::dppic::Dppic { + pac::$inst + } + } impl crate::ppi::Channel for peripherals::$type { fn number(&self) -> usize { $number } } }; - ($type:ident, $number:expr => static) => { - impl_ppi_channel!($type, $number); + ($type:ident, $inst:ident, $number:expr => static) => { + impl_ppi_channel!($type, $inst, $number); impl crate::ppi::StaticChannel for peripherals::$type {} impl From for crate::ppi::AnyStaticChannel { fn from(val: peripherals::$type) -> Self { Self { number: crate::ppi::Channel::number(&val) as u8, + #[cfg(feature = "_dppi")] + regs: pac::$inst, } } } }; - ($type:ident, $number:expr => configurable) => { - impl_ppi_channel!($type, $number); + ($type:ident, $inst:ident, $number:expr => configurable) => { + impl_ppi_channel!($type, $inst, $number); impl crate::ppi::ConfigurableChannel for peripherals::$type {} impl From for crate::ppi::AnyConfigurableChannel { fn from(val: peripherals::$type) -> Self { Self { number: crate::ppi::Channel::number(&val) as u8, + #[cfg(feature = "_dppi")] + regs: pac::$inst, } } } @@ -304,40 +337,54 @@ macro_rules! impl_ppi_channel { /// A type erased PPI group. pub struct AnyGroup { - number: u8, + pub(crate) number: u8, + #[cfg(feature = "_dppi")] + pub(crate) regs: pac::dppic::Dppic, + #[cfg(not(feature = "_dppi"))] + pub(crate) regs: pac::ppi::Ppi, } impl_peripheral!(AnyGroup); -impl SealedGroup for AnyGroup {} +impl SealedGroup for AnyGroup { + #[cfg(feature = "_dppi")] + fn regs(&self) -> pac::dppic::Dppic { + self.regs + } + #[cfg(not(feature = "_dppi"))] + fn regs(&self) -> pac::ppi::Ppi { + self.regs + } +} impl Group for AnyGroup { fn number(&self) -> usize { self.number as usize } } -macro_rules! impl_group { - ($type:ident, $number:expr) => { - impl SealedGroup for peripherals::$type {} - impl Group for peripherals::$type { +macro_rules! impl_ppi_group { + ($type:ident, $inst:ident, $number:expr) => { + impl crate::ppi::SealedGroup for crate::peripherals::$type { + #[cfg(feature = "_dppi")] + fn regs(&self) -> pac::dppic::Dppic { + pac::$inst + } + #[cfg(not(feature = "_dppi"))] + fn regs(&self) -> pac::ppi::Ppi { + pac::$inst + } + } + impl crate::ppi::Group for crate::peripherals::$type { fn number(&self) -> usize { $number } } - impl From for crate::ppi::AnyGroup { - fn from(val: peripherals::$type) -> Self { + impl From for crate::ppi::AnyGroup { + fn from(val: crate::peripherals::$type) -> Self { Self { number: crate::ppi::Group::number(&val) as u8, + regs: pac::$inst, } } } }; } - -impl_group!(PPI_GROUP0, 0); -impl_group!(PPI_GROUP1, 1); -impl_group!(PPI_GROUP2, 2); -impl_group!(PPI_GROUP3, 3); -#[cfg(not(feature = "_nrf51"))] -impl_group!(PPI_GROUP4, 4); -#[cfg(not(feature = "_nrf51"))] -impl_group!(PPI_GROUP5, 5); diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 00b3278c7..04eb14a77 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -203,7 +203,7 @@ impl<'d> SequencePwm<'d> { /// Interacting with the sequence while it runs puts it in an unknown state #[inline(always)] pub unsafe fn task_start_seq0(&self) -> Task<'d> { - Task::from_reg(self.r.tasks_seqstart(0)) + Task::from_reg(self.r.tasks_dma().seq(0).start()) } /// Returns reference to `Seq1 Started` task endpoint for PPI. @@ -212,7 +212,7 @@ impl<'d> SequencePwm<'d> { /// Interacting with the sequence while it runs puts it in an unknown state #[inline(always)] pub unsafe fn task_start_seq1(&self) -> Task<'d> { - Task::from_reg(self.r.tasks_seqstart(1)) + Task::from_reg(self.r.tasks_dma().seq(1).start()) } /// Returns reference to `NextStep` task endpoint for PPI. @@ -444,6 +444,21 @@ pub struct Sequencer<'d, 's> { sequence1: Option>, } +#[cfg(feature = "_nrf54l")] +fn pwmseq(r: pac::pwm::Pwm, n: usize) -> pac::pwm::PwmSeq { + r.seq(n) +} + +#[cfg(not(feature = "_nrf54l"))] +fn pwmseq(r: pac::pwm::Pwm, n: usize) -> pac::pwm::DmaSeq { + r.dma().seq(n) +} + +#[cfg(feature = "_nrf54l")] +const CNT_UNIT: u32 = 2; +#[cfg(not(feature = "_nrf54l"))] +const CNT_UNIT: u32 = 1; + impl<'d, 's> Sequencer<'d, 's> { /// Create a new double sequence. In the absence of sequence 1, sequence 0 /// will be used twice in the one loop. @@ -476,15 +491,21 @@ impl<'d, 's> Sequencer<'d, 's> { let r = self._pwm.r; - r.seq(0).refresh().write(|w| w.0 = sequence0.config.refresh); - r.seq(0).enddelay().write(|w| w.0 = sequence0.config.end_delay); - r.seq(0).ptr().write_value(sequence0.words.as_ptr() as u32); - r.seq(0).cnt().write(|w| w.0 = sequence0.words.len() as u32); - - r.seq(1).refresh().write(|w| w.0 = alt_sequence.config.refresh); - r.seq(1).enddelay().write(|w| w.0 = alt_sequence.config.end_delay); - r.seq(1).ptr().write_value(alt_sequence.words.as_ptr() as u32); - r.seq(1).cnt().write(|w| w.0 = alt_sequence.words.len() as u32); + pwmseq(r, 0).refresh().write(|w| w.0 = sequence0.config.refresh); + pwmseq(r, 0).enddelay().write(|w| w.0 = sequence0.config.end_delay); + r.dma().seq(0).ptr().write_value(sequence0.words.as_ptr() as u32); + r.dma() + .seq(0) + .maxcnt() + .write(|w| w.0 = sequence0.words.len() as u32 * CNT_UNIT); + + pwmseq(r, 1).refresh().write(|w| w.0 = alt_sequence.config.refresh); + pwmseq(r, 1).enddelay().write(|w| w.0 = alt_sequence.config.end_delay); + r.dma().seq(1).ptr().write_value(alt_sequence.words.as_ptr() as u32); + r.dma() + .seq(1) + .maxcnt() + .write(|w| w.0 = alt_sequence.words.len() as u32 * CNT_UNIT); r.enable().write(|w| w.set_enable(true)); @@ -500,11 +521,11 @@ impl<'d, 's> Sequencer<'d, 's> { // to play infinitely, repeat the sequence one time, then have loops done self trigger seq0 again SequenceMode::Infinite => { r.loop_().write(|w| w.set_cnt(vals::LoopCnt::from_bits(1))); - r.shorts().write(|w| w.set_loopsdone_seqstart0(true)); + r.shorts().write(|w| w.set_loopsdone_dma_seq0_start(true)); } } - r.tasks_seqstart(seqstart_index).write_value(1); + r.tasks_dma().seq(seqstart_index).start().write_value(1); Ok(()) } @@ -781,10 +802,10 @@ impl<'d> SimplePwm<'d> { // Enable r.enable().write(|w| w.set_enable(true)); - r.seq(0).ptr().write_value((pwm.duty).as_ptr() as u32); - r.seq(0).cnt().write(|w| w.0 = 4); - r.seq(0).refresh().write(|w| w.0 = 0); - r.seq(0).enddelay().write(|w| w.0 = 0); + r.dma().seq(0).ptr().write_value((pwm.duty).as_ptr() as u32); + r.dma().seq(0).maxcnt().write(|w| w.0 = 4 * CNT_UNIT); + pwmseq(r, 0).refresh().write(|w| w.0 = 0); + pwmseq(r, 0).enddelay().write(|w| w.0 = 0); r.decoder().write(|w| { w.set_load(vals::Load::INDIVIDUAL); @@ -846,7 +867,7 @@ impl<'d> SimplePwm<'d> { /// Transfer the duty cycles from `self` to the peripheral. fn sync_duty_cyles_to_peripheral(&self) { // reload ptr in case self was moved - self.r.seq(0).ptr().write_value((self.duty).as_ptr() as u32); + self.r.dma().seq(0).ptr().write_value((self.duty).as_ptr() as u32); // defensive before seqstart compiler_fence(Ordering::SeqCst); @@ -854,7 +875,7 @@ impl<'d> SimplePwm<'d> { self.r.events_seqend(0).write_value(0); // tasks_seqstart() doesn't exist in all svds so write its bit instead - self.r.tasks_seqstart(0).write_value(1); + self.r.tasks_dma().seq(0).start().write_value(1); // defensive wait until waveform is loaded after seqstart so set_duty // can't be called again while dma is still reading diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index a199c1c4d..ca8cbd73e 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -10,6 +10,7 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{Peri, impl_peripheral}; use embassy_sync::waitqueue::AtomicWaker; +#[cfg(not(feature = "_nrf54l"))] pub(crate) use vals::Psel as InputChannel; use crate::interrupt::InterruptExt; @@ -84,6 +85,7 @@ pub struct ChannelConfig<'d> { /// Gain used to control the effective input range of the SAADC. pub gain: Gain, /// Positive channel resistor control. + #[cfg(not(feature = "_nrf54l"))] pub resistor: Resistor, /// Acquisition time in microseconds. pub time: Time, @@ -98,7 +100,11 @@ impl<'d> ChannelConfig<'d> { pub fn single_ended(input: impl Input + 'd) -> Self { Self { reference: Reference::INTERNAL, + #[cfg(not(feature = "_nrf54l"))] gain: Gain::GAIN1_6, + #[cfg(feature = "_nrf54l")] + gain: Gain::GAIN2_8, + #[cfg(not(feature = "_nrf54l"))] resistor: Resistor::BYPASS, time: Time::_10US, p_channel: input.degrade_saadc(), @@ -109,7 +115,11 @@ impl<'d> ChannelConfig<'d> { pub fn differential(p_input: impl Input + 'd, n_input: impl Input + 'd) -> Self { Self { reference: Reference::INTERNAL, + #[cfg(not(feature = "_nrf54l"))] gain: Gain::GAIN1_6, + #[cfg(feature = "_nrf54l")] + gain: Gain::GAIN2_8, + #[cfg(not(feature = "_nrf54l"))] resistor: Resistor::BYPASS, time: Time::_10US, p_channel: p_input.degrade_saadc(), @@ -118,6 +128,8 @@ impl<'d> ChannelConfig<'d> { } } +const CNT_UNIT: usize = if cfg!(feature = "_nrf54l") { 2 } else { 1 }; + /// Value returned by the SAADC callback, deciding what happens next. #[derive(PartialEq)] pub enum CallbackResult { @@ -150,19 +162,38 @@ impl<'d, const N: usize> Saadc<'d, N> { r.oversample().write(|w| w.set_oversample(oversample.into())); for (i, cc) in channel_configs.iter().enumerate() { + #[cfg(not(feature = "_nrf54l"))] r.ch(i).pselp().write(|w| w.set_pselp(cc.p_channel.channel())); + #[cfg(feature = "_nrf54l")] + r.ch(i).pselp().write(|w| { + w.set_port(cc.p_channel.port()); + w.set_pin(cc.p_channel.pin()); + w.set_internal(cc.p_channel.internal()); + w.set_connect(cc.p_channel.connect()); + }); if let Some(n_channel) = &cc.n_channel { + #[cfg(not(feature = "_nrf54l"))] r.ch(i).pseln().write(|w| w.set_pseln(n_channel.channel())); + #[cfg(feature = "_nrf54l")] + r.ch(i).pseln().write(|w| { + w.set_port(n_channel.port()); + w.set_pin(n_channel.pin()); + w.set_connect(n_channel.connect().to_bits().into()); + }); } r.ch(i).config().write(|w| { w.set_refsel(cc.reference.into()); w.set_gain(cc.gain.into()); w.set_tacq(cc.time.into()); + #[cfg(feature = "_nrf54l")] + w.set_tconv(7); // 7 is the default from the Nordic C driver w.set_mode(match cc.n_channel { None => vals::ConfigMode::SE, Some(_) => vals::ConfigMode::DIFF, }); + #[cfg(not(feature = "_nrf54l"))] w.set_resp(cc.resistor.into()); + #[cfg(not(feature = "_nrf54l"))] w.set_resn(vals::Resn::BYPASS); w.set_burst(!matches!(oversample, Oversample::BYPASS)); }); @@ -222,7 +253,7 @@ impl<'d, const N: usize> Saadc<'d, N> { // Set up the DMA r.result().ptr().write_value(buf.as_mut_ptr() as u32); - r.result().maxcnt().write(|w| w.set_maxcnt(N as _)); + r.result().maxcnt().write(|w| w.set_maxcnt((N * CNT_UNIT) as _)); // Reset and enable the end event r.events_end().write_value(0); @@ -354,7 +385,7 @@ impl<'d, const N: usize> Saadc<'d, N> { // Set up the initial DMA r.result().ptr().write_value(bufs[0].as_mut_ptr() as u32); - r.result().maxcnt().write(|w| w.set_maxcnt((N0 * N) as _)); + r.result().maxcnt().write(|w| w.set_maxcnt((N0 * N * CNT_UNIT) as _)); // Reset and enable the events r.events_end().write_value(0); @@ -473,12 +504,21 @@ impl<'d, const N: usize> Drop for Saadc<'d, N> { let r = Self::regs(); r.enable().write(|w| w.set_enable(false)); for i in 0..N { - r.ch(i).pselp().write(|w| w.set_pselp(InputChannel::NC)); - r.ch(i).pseln().write(|w| w.set_pseln(InputChannel::NC)); + #[cfg(not(feature = "_nrf54l"))] + { + r.ch(i).pselp().write(|w| w.set_pselp(InputChannel::NC)); + r.ch(i).pseln().write(|w| w.set_pseln(InputChannel::NC)); + } + #[cfg(feature = "_nrf54l")] + { + r.ch(i).pselp().write(|w| w.set_connect(vals::PselpConnect::NC)); + r.ch(i).pseln().write(|w| w.set_connect(vals::PselnConnect::NC)); + } } } } +#[cfg(not(feature = "_nrf54l"))] impl From for vals::Gain { fn from(gain: Gain) -> Self { match gain { @@ -494,7 +534,24 @@ impl From for vals::Gain { } } +#[cfg(feature = "_nrf54l")] +impl From for vals::Gain { + fn from(gain: Gain) -> Self { + match gain { + Gain::GAIN2_8 => vals::Gain::GAIN2_8, + Gain::GAIN2_7 => vals::Gain::GAIN2_7, + Gain::GAIN2_6 => vals::Gain::GAIN2_6, + Gain::GAIN2_5 => vals::Gain::GAIN2_5, + Gain::GAIN2_4 => vals::Gain::GAIN2_4, + Gain::GAIN2_3 => vals::Gain::GAIN2_3, + Gain::GAIN1 => vals::Gain::GAIN1, + Gain::GAIN2 => vals::Gain::GAIN2, + } + } +} + /// Gain control +#[cfg(not(feature = "_nrf54l"))] #[non_exhaustive] #[derive(Clone, Copy)] pub enum Gain { @@ -516,11 +573,37 @@ pub enum Gain { GAIN4 = 7, } +/// Gain control +#[cfg(feature = "_nrf54l")] +#[non_exhaustive] +#[derive(Clone, Copy)] +pub enum Gain { + /// 2/8 + GAIN2_8 = 0, + /// 2/7 + GAIN2_7 = 1, + /// 2/6 + GAIN2_6 = 2, + /// 2/5 + GAIN2_5 = 3, + /// 2/4 + GAIN2_4 = 4, + /// 2/3 + GAIN2_3 = 5, + /// 1 + GAIN1 = 6, + /// 2 + GAIN2 = 7, +} + impl From for vals::Refsel { fn from(reference: Reference) -> Self { match reference { Reference::INTERNAL => vals::Refsel::INTERNAL, + #[cfg(not(feature = "_nrf54l"))] Reference::VDD1_4 => vals::Refsel::VDD1_4, + #[cfg(feature = "_nrf54l")] + Reference::EXTERNAL => vals::Refsel::EXTERNAL, } } } @@ -531,10 +614,15 @@ impl From for vals::Refsel { pub enum Reference { /// Internal reference (0.6 V) INTERNAL = 0, + #[cfg(not(feature = "_nrf54l"))] /// VDD/4 as reference VDD1_4 = 1, + /// PADC_EXT_REF_1V2 as reference + #[cfg(feature = "_nrf54l")] + EXTERNAL = 1, } +#[cfg(not(feature = "_nrf54l"))] impl From for vals::Resp { fn from(resistor: Resistor) -> Self { match resistor { @@ -549,6 +637,7 @@ impl From for vals::Resp { /// Positive channel resistor control #[non_exhaustive] #[derive(Clone, Copy)] +#[cfg(not(feature = "_nrf54l"))] pub enum Resistor { /// Bypass resistor ladder BYPASS = 0, @@ -560,6 +649,7 @@ pub enum Resistor { VDD1_2 = 3, } +#[cfg(not(feature = "_nrf54l"))] impl From