From 65628e1f1538f003c114d97c98970631953e7d6e Mon Sep 17 00:00:00 2001
From: Dion Dokter <dion@tweedegolf.com>
Date: Wed, 13 Oct 2021 16:35:05 +0200
Subject: [PATCH 01/10] - Added _ppi and _dppi to distinguish between the new
 and the old peripheral. - Removed ConfigurableChannel and added capacity
 numbers to the channels - Replaced the PPI api with a new one using the DPPI
 terminology (publish & subscribe) - Updated all tasks and event registers for
 DPPI

---
 embassy-nrf/Cargo.toml            |  18 +-
 embassy-nrf/src/buffered_uarte.rs |  58 ++--
 embassy-nrf/src/chips/nrf52805.rs |  44 +--
 embassy-nrf/src/chips/nrf52810.rs |  64 ++---
 embassy-nrf/src/chips/nrf52811.rs |  64 ++---
 embassy-nrf/src/chips/nrf52820.rs |  64 ++---
 embassy-nrf/src/chips/nrf52832.rs |  64 ++---
 embassy-nrf/src/chips/nrf52833.rs |  64 ++---
 embassy-nrf/src/chips/nrf52840.rs |  64 ++---
 embassy-nrf/src/gpiote.rs         |  26 +-
 embassy-nrf/src/ppi.rs            | 426 +++++++++++++++++++++++-------
 embassy-nrf/src/timer.rs          |  35 ++-
 embassy-nrf/src/uarte.rs          |  57 ++--
 examples/nrf/src/bin/ppi.rs       |  18 +-
 14 files changed, 696 insertions(+), 370 deletions(-)

diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index fc1ec6b0..c083978c 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -18,18 +18,20 @@ defmt-info = [ ]
 defmt-warn = [ ]
 defmt-error = [ ]
 
-nrf52805 = ["nrf52805-pac"]
-nrf52810 = ["nrf52810-pac"]
-nrf52811 = ["nrf52811-pac"]
-nrf52820 = ["nrf52820-pac"]
-nrf52832 = ["nrf52832-pac"]
-nrf52833 = ["nrf52833-pac"]
-nrf52840 = ["nrf52840-pac"]
-nrf9160 = ["nrf9160-pac"]
+nrf52805 = ["nrf52805-pac", "_ppi"]
+nrf52810 = ["nrf52810-pac", "_ppi"]
+nrf52811 = ["nrf52811-pac", "_ppi"]
+nrf52820 = ["nrf52820-pac", "_ppi"]
+nrf52832 = ["nrf52832-pac", "_ppi"]
+nrf52833 = ["nrf52833-pac", "_ppi"]
+nrf52840 = ["nrf52840-pac", "_ppi"]
+nrf9160 = ["nrf9160-pac", "_dppi"]
 
 # Features starting with `_` are for internal use only. They're not intended
 # to be enabled by other crates, and are not covered by semver guarantees.
 _time-driver = ["embassy/time-tick-32768hz"]
+_ppi = []
+_dppi = []
 
 gpiote = []
 time-driver-rtc1 = ["_time-driver"]
diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs
index cd08875c..f7e55e42 100644
--- a/embassy-nrf/src/buffered_uarte.rs
+++ b/embassy-nrf/src/buffered_uarte.rs
@@ -5,7 +5,7 @@ use core::pin::Pin;
 use core::sync::atomic::{compiler_fence, Ordering};
 use core::task::{Context, Poll};
 use embassy::interrupt::InterruptExt;
-use embassy::io::{AsyncBufRead, AsyncWrite, Result};
+use embassy::io::{AsyncBufRead, AsyncWrite};
 use embassy::util::Unborrow;
 use embassy::waitqueue::WakerRegistration;
 use embassy_hal_common::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
@@ -14,15 +14,27 @@ use embassy_hal_common::{low_power_wait_until, unborrow};
 
 use crate::gpio::sealed::Pin as _;
 use crate::gpio::{OptionalPin as GpioOptionalPin, Pin as GpioPin};
-use crate::pac;
-use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};
+use crate::ppi::{AnyChannel, Channel, Event, Ppi, Task};
 use crate::timer::Instance as TimerInstance;
 use crate::timer::{Frequency, Timer};
 use crate::uarte::{Config, Instance as UarteInstance};
+use crate::{pac, ppi};
 
 // Re-export SVD variants to allow user to directly set values
 pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
 
+#[non_exhaustive]
+#[derive(Clone, Debug)]
+pub enum Error {
+    PpiError(ppi::Error),
+}
+
+impl From<ppi::Error> for Error {
+    fn from(e: ppi::Error) -> Self {
+        Self::PpiError(e)
+    }
+}
+
 #[derive(Copy, Clone, Debug, PartialEq)]
 enum RxState {
     Idle,
@@ -45,8 +57,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> State<'d, U, T> {
 struct StateInner<'d, U: UarteInstance, T: TimerInstance> {
     phantom: PhantomData<&'d mut U>,
     timer: Timer<'d, T>,
-    _ppi_ch1: Ppi<'d, AnyConfigurableChannel>,
-    _ppi_ch2: Ppi<'d, AnyConfigurableChannel>,
+    _ppi_ch1: Ppi<'d, AnyChannel>,
+    _ppi_ch2: Ppi<'d, AnyChannel>,
 
     rx: RingBuffer<'d>,
     rx_state: RxState,
@@ -66,12 +78,15 @@ impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {
 
 impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
     /// unsafe: may not leak self or futures
+    ///
+    /// - *Note:* ppi_ch1 must have at least 1 free event and 2 free tasks or a PPI error is returned
+    /// - *Note:* ppi_ch2 must have at least 1 free event and 1 free tasks or a PPI error is returned
     pub unsafe fn new(
         state: &'d mut State<'d, U, T>,
         _uarte: impl Unborrow<Target = U> + 'd,
         timer: impl Unborrow<Target = T> + 'd,
-        ppi_ch1: impl Unborrow<Target = impl ConfigurableChannel> + 'd,
-        ppi_ch2: impl Unborrow<Target = impl ConfigurableChannel> + 'd,
+        ppi_ch1: impl Unborrow<Target = impl Channel> + 'd,
+        ppi_ch2: impl Unborrow<Target = impl Channel> + 'd,
         irq: impl Unborrow<Target = U::Interrupt> + 'd,
         rxd: impl Unborrow<Target = impl GpioPin> + 'd,
         txd: impl Unborrow<Target = impl GpioPin> + 'd,
@@ -80,7 +95,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
         config: Config,
         rx_buffer: &'d mut [u8],
         tx_buffer: &'d mut [u8],
-    ) -> Self {
+    ) -> Result<Self, Error> {
         unborrow!(ppi_ch1, ppi_ch2, irq, rxd, txd, cts, rts);
 
         let r = U::regs();
@@ -144,18 +159,18 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
         timer.cc(0).short_compare_clear();
         timer.cc(0).short_compare_stop();
 
-        let mut ppi_ch1 = Ppi::new(ppi_ch1.degrade_configurable());
-        ppi_ch1.set_event(Event::from_reg(&r.events_rxdrdy));
-        ppi_ch1.set_task(timer.task_clear());
-        ppi_ch1.set_fork_task(timer.task_start());
+        let mut ppi_ch1 = Ppi::new(ppi_ch1.degrade());
+        ppi_ch1.publish(Event::from_reg(&r.events_rxdrdy))?;
+        ppi_ch1.subscribe(timer.task_clear())?;
+        ppi_ch1.subscribe(timer.task_start())?;
         ppi_ch1.enable();
 
-        let mut ppi_ch2 = Ppi::new(ppi_ch2.degrade_configurable());
-        ppi_ch2.set_event(timer.cc(0).event_compare());
-        ppi_ch2.set_task(Task::from_reg(&r.tasks_stoprx));
+        let mut ppi_ch2 = Ppi::new(ppi_ch2.degrade());
+        ppi_ch2.publish(timer.cc(0).event_compare())?;
+        ppi_ch2.subscribe(Task::from_reg(&r.tasks_stoprx))?;
         ppi_ch2.enable();
 
-        Self {
+        Ok(Self {
             inner: PeripheralMutex::new_unchecked(irq, &mut state.0, move || StateInner {
                 phantom: PhantomData,
                 timer,
@@ -170,7 +185,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
                 tx_state: TxState::Idle,
                 tx_waker: WakerRegistration::new(),
             }),
-        }
+        })
     }
 
     pub fn set_baudrate(&mut self, baudrate: Baudrate) {
@@ -187,7 +202,10 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
 }
 
 impl<'d, U: UarteInstance, T: TimerInstance> AsyncBufRead for BufferedUarte<'d, U, T> {
-    fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
+    fn poll_fill_buf(
+        mut self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+    ) -> Poll<embassy::io::Result<&[u8]>> {
         self.inner.with(|state| {
             // Conservative compiler fence to prevent optimizations that do not
             // take in to account actions by DMA. The fence has been placed here,
@@ -206,7 +224,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> AsyncBufRead for BufferedUarte<'d,
 
             trace!("  empty");
             state.rx_waker.register(cx.waker());
-            Poll::<Result<&[u8]>>::Pending
+            Poll::<embassy::io::Result<&[u8]>>::Pending
         })
     }
 
@@ -224,7 +242,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> AsyncWrite for BufferedUarte<'d, U,
         mut self: Pin<&mut Self>,
         cx: &mut Context<'_>,
         buf: &[u8],
-    ) -> Poll<Result<usize>> {
+    ) -> Poll<embassy::io::Result<usize>> {
         let poll = self.inner.with(|state| {
             trace!("poll_write: {:?}", buf.len());
 
diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs
index d6869793..03fc5f30 100644
--- a/embassy-nrf/src/chips/nrf52805.rs
+++ b/embassy-nrf/src/chips/nrf52805.rs
@@ -167,28 +167,28 @@ 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);
-impl_ppi_channel!(PPI_CH21, 21);
-impl_ppi_channel!(PPI_CH22, 22);
-impl_ppi_channel!(PPI_CH23, 23);
-impl_ppi_channel!(PPI_CH24, 24);
-impl_ppi_channel!(PPI_CH25, 25);
-impl_ppi_channel!(PPI_CH26, 26);
-impl_ppi_channel!(PPI_CH27, 27);
-impl_ppi_channel!(PPI_CH28, 28);
-impl_ppi_channel!(PPI_CH29, 29);
-impl_ppi_channel!(PPI_CH30, 30);
-impl_ppi_channel!(PPI_CH31, 31);
+impl_ppi_channel!(PPI_CH0, 0, 2, 1);
+impl_ppi_channel!(PPI_CH1, 1, 2, 1);
+impl_ppi_channel!(PPI_CH2, 2, 2, 1);
+impl_ppi_channel!(PPI_CH3, 3, 2, 1);
+impl_ppi_channel!(PPI_CH4, 4, 2, 1);
+impl_ppi_channel!(PPI_CH5, 5, 2, 1);
+impl_ppi_channel!(PPI_CH6, 6, 2, 1);
+impl_ppi_channel!(PPI_CH7, 7, 2, 1);
+impl_ppi_channel!(PPI_CH8, 8, 2, 1);
+impl_ppi_channel!(PPI_CH9, 9, 2, 1);
+impl_ppi_channel!(PPI_CH20, 20, 1, 0);
+impl_ppi_channel!(PPI_CH21, 21, 1, 0);
+impl_ppi_channel!(PPI_CH22, 22, 1, 0);
+impl_ppi_channel!(PPI_CH23, 23, 1, 0);
+impl_ppi_channel!(PPI_CH24, 24, 1, 0);
+impl_ppi_channel!(PPI_CH25, 25, 1, 0);
+impl_ppi_channel!(PPI_CH26, 26, 1, 0);
+impl_ppi_channel!(PPI_CH27, 27, 1, 0);
+impl_ppi_channel!(PPI_CH28, 28, 1, 0);
+impl_ppi_channel!(PPI_CH29, 29, 1, 0);
+impl_ppi_channel!(PPI_CH30, 30, 1, 0);
+impl_ppi_channel!(PPI_CH31, 31, 1, 0);
 
 impl_saadc_input!(P0_04, ANALOGINPUT2);
 impl_saadc_input!(P0_05, ANALOGINPUT3);
diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs
index c119e7cd..e317e4c5 100644
--- a/embassy-nrf/src/chips/nrf52810.rs
+++ b/embassy-nrf/src/chips/nrf52810.rs
@@ -172,38 +172,38 @@ 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);
-impl_ppi_channel!(PPI_CH21, 21);
-impl_ppi_channel!(PPI_CH22, 22);
-impl_ppi_channel!(PPI_CH23, 23);
-impl_ppi_channel!(PPI_CH24, 24);
-impl_ppi_channel!(PPI_CH25, 25);
-impl_ppi_channel!(PPI_CH26, 26);
-impl_ppi_channel!(PPI_CH27, 27);
-impl_ppi_channel!(PPI_CH28, 28);
-impl_ppi_channel!(PPI_CH29, 29);
-impl_ppi_channel!(PPI_CH30, 30);
-impl_ppi_channel!(PPI_CH31, 31);
+impl_ppi_channel!(PPI_CH0, 0, 2, 1);
+impl_ppi_channel!(PPI_CH1, 1, 2, 1);
+impl_ppi_channel!(PPI_CH2, 2, 2, 1);
+impl_ppi_channel!(PPI_CH3, 3, 2, 1);
+impl_ppi_channel!(PPI_CH4, 4, 2, 1);
+impl_ppi_channel!(PPI_CH5, 5, 2, 1);
+impl_ppi_channel!(PPI_CH6, 6, 2, 1);
+impl_ppi_channel!(PPI_CH7, 7, 2, 1);
+impl_ppi_channel!(PPI_CH8, 8, 2, 1);
+impl_ppi_channel!(PPI_CH9, 9, 2, 1);
+impl_ppi_channel!(PPI_CH10, 10, 2, 1);
+impl_ppi_channel!(PPI_CH11, 11, 2, 1);
+impl_ppi_channel!(PPI_CH12, 12, 2, 1);
+impl_ppi_channel!(PPI_CH13, 13, 2, 1);
+impl_ppi_channel!(PPI_CH14, 14, 2, 1);
+impl_ppi_channel!(PPI_CH15, 15, 2, 1);
+impl_ppi_channel!(PPI_CH16, 16, 2, 1);
+impl_ppi_channel!(PPI_CH17, 17, 2, 1);
+impl_ppi_channel!(PPI_CH18, 18, 2, 1);
+impl_ppi_channel!(PPI_CH19, 19, 2, 1);
+impl_ppi_channel!(PPI_CH20, 20, 1, 0);
+impl_ppi_channel!(PPI_CH21, 21, 1, 0);
+impl_ppi_channel!(PPI_CH22, 22, 1, 0);
+impl_ppi_channel!(PPI_CH23, 23, 1, 0);
+impl_ppi_channel!(PPI_CH24, 24, 1, 0);
+impl_ppi_channel!(PPI_CH25, 25, 1, 0);
+impl_ppi_channel!(PPI_CH26, 26, 1, 0);
+impl_ppi_channel!(PPI_CH27, 27, 1, 0);
+impl_ppi_channel!(PPI_CH28, 28, 1, 0);
+impl_ppi_channel!(PPI_CH29, 29, 1, 0);
+impl_ppi_channel!(PPI_CH30, 30, 1, 0);
+impl_ppi_channel!(PPI_CH31, 31, 1, 0);
 
 impl_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs
index 3c9df40f..748f5f4b 100644
--- a/embassy-nrf/src/chips/nrf52811.rs
+++ b/embassy-nrf/src/chips/nrf52811.rs
@@ -173,38 +173,38 @@ 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);
-impl_ppi_channel!(PPI_CH21, 21);
-impl_ppi_channel!(PPI_CH22, 22);
-impl_ppi_channel!(PPI_CH23, 23);
-impl_ppi_channel!(PPI_CH24, 24);
-impl_ppi_channel!(PPI_CH25, 25);
-impl_ppi_channel!(PPI_CH26, 26);
-impl_ppi_channel!(PPI_CH27, 27);
-impl_ppi_channel!(PPI_CH28, 28);
-impl_ppi_channel!(PPI_CH29, 29);
-impl_ppi_channel!(PPI_CH30, 30);
-impl_ppi_channel!(PPI_CH31, 31);
+impl_ppi_channel!(PPI_CH0, 0, 2, 1);
+impl_ppi_channel!(PPI_CH1, 1, 2, 1);
+impl_ppi_channel!(PPI_CH2, 2, 2, 1);
+impl_ppi_channel!(PPI_CH3, 3, 2, 1);
+impl_ppi_channel!(PPI_CH4, 4, 2, 1);
+impl_ppi_channel!(PPI_CH5, 5, 2, 1);
+impl_ppi_channel!(PPI_CH6, 6, 2, 1);
+impl_ppi_channel!(PPI_CH7, 7, 2, 1);
+impl_ppi_channel!(PPI_CH8, 8, 2, 1);
+impl_ppi_channel!(PPI_CH9, 9, 2, 1);
+impl_ppi_channel!(PPI_CH10, 10, 2, 1);
+impl_ppi_channel!(PPI_CH11, 11, 2, 1);
+impl_ppi_channel!(PPI_CH12, 12, 2, 1);
+impl_ppi_channel!(PPI_CH13, 13, 2, 1);
+impl_ppi_channel!(PPI_CH14, 14, 2, 1);
+impl_ppi_channel!(PPI_CH15, 15, 2, 1);
+impl_ppi_channel!(PPI_CH16, 16, 2, 1);
+impl_ppi_channel!(PPI_CH17, 17, 2, 1);
+impl_ppi_channel!(PPI_CH18, 18, 2, 1);
+impl_ppi_channel!(PPI_CH19, 19, 2, 1);
+impl_ppi_channel!(PPI_CH20, 20, 1, 0);
+impl_ppi_channel!(PPI_CH21, 21, 1, 0);
+impl_ppi_channel!(PPI_CH22, 22, 1, 0);
+impl_ppi_channel!(PPI_CH23, 23, 1, 0);
+impl_ppi_channel!(PPI_CH24, 24, 1, 0);
+impl_ppi_channel!(PPI_CH25, 25, 1, 0);
+impl_ppi_channel!(PPI_CH26, 26, 1, 0);
+impl_ppi_channel!(PPI_CH27, 27, 1, 0);
+impl_ppi_channel!(PPI_CH28, 28, 1, 0);
+impl_ppi_channel!(PPI_CH29, 29, 1, 0);
+impl_ppi_channel!(PPI_CH30, 30, 1, 0);
+impl_ppi_channel!(PPI_CH31, 31, 1, 0);
 
 impl_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs
index 8a8e23a9..02d2c610 100644
--- a/embassy-nrf/src/chips/nrf52820.rs
+++ b/embassy-nrf/src/chips/nrf52820.rs
@@ -168,38 +168,38 @@ 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);
-impl_ppi_channel!(PPI_CH21, 21);
-impl_ppi_channel!(PPI_CH22, 22);
-impl_ppi_channel!(PPI_CH23, 23);
-impl_ppi_channel!(PPI_CH24, 24);
-impl_ppi_channel!(PPI_CH25, 25);
-impl_ppi_channel!(PPI_CH26, 26);
-impl_ppi_channel!(PPI_CH27, 27);
-impl_ppi_channel!(PPI_CH28, 28);
-impl_ppi_channel!(PPI_CH29, 29);
-impl_ppi_channel!(PPI_CH30, 30);
-impl_ppi_channel!(PPI_CH31, 31);
+impl_ppi_channel!(PPI_CH0, 0, 2, 1);
+impl_ppi_channel!(PPI_CH1, 1, 2, 1);
+impl_ppi_channel!(PPI_CH2, 2, 2, 1);
+impl_ppi_channel!(PPI_CH3, 3, 2, 1);
+impl_ppi_channel!(PPI_CH4, 4, 2, 1);
+impl_ppi_channel!(PPI_CH5, 5, 2, 1);
+impl_ppi_channel!(PPI_CH6, 6, 2, 1);
+impl_ppi_channel!(PPI_CH7, 7, 2, 1);
+impl_ppi_channel!(PPI_CH8, 8, 2, 1);
+impl_ppi_channel!(PPI_CH9, 9, 2, 1);
+impl_ppi_channel!(PPI_CH10, 10, 2, 1);
+impl_ppi_channel!(PPI_CH11, 11, 2, 1);
+impl_ppi_channel!(PPI_CH12, 12, 2, 1);
+impl_ppi_channel!(PPI_CH13, 13, 2, 1);
+impl_ppi_channel!(PPI_CH14, 14, 2, 1);
+impl_ppi_channel!(PPI_CH15, 15, 2, 1);
+impl_ppi_channel!(PPI_CH16, 16, 2, 1);
+impl_ppi_channel!(PPI_CH17, 17, 2, 1);
+impl_ppi_channel!(PPI_CH18, 18, 2, 1);
+impl_ppi_channel!(PPI_CH19, 19, 2, 1);
+impl_ppi_channel!(PPI_CH20, 20, 1, 0);
+impl_ppi_channel!(PPI_CH21, 21, 1, 0);
+impl_ppi_channel!(PPI_CH22, 22, 1, 0);
+impl_ppi_channel!(PPI_CH23, 23, 1, 0);
+impl_ppi_channel!(PPI_CH24, 24, 1, 0);
+impl_ppi_channel!(PPI_CH25, 25, 1, 0);
+impl_ppi_channel!(PPI_CH26, 26, 1, 0);
+impl_ppi_channel!(PPI_CH27, 27, 1, 0);
+impl_ppi_channel!(PPI_CH28, 28, 1, 0);
+impl_ppi_channel!(PPI_CH29, 29, 1, 0);
+impl_ppi_channel!(PPI_CH30, 30, 1, 0);
+impl_ppi_channel!(PPI_CH31, 31, 1, 0);
 
 pub mod irqs {
     use crate::pac::Interrupt as InterruptEnum;
diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs
index c1b02a0b..611dbbd5 100644
--- a/embassy-nrf/src/chips/nrf52832.rs
+++ b/embassy-nrf/src/chips/nrf52832.rs
@@ -190,38 +190,38 @@ 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);
-impl_ppi_channel!(PPI_CH21, 21);
-impl_ppi_channel!(PPI_CH22, 22);
-impl_ppi_channel!(PPI_CH23, 23);
-impl_ppi_channel!(PPI_CH24, 24);
-impl_ppi_channel!(PPI_CH25, 25);
-impl_ppi_channel!(PPI_CH26, 26);
-impl_ppi_channel!(PPI_CH27, 27);
-impl_ppi_channel!(PPI_CH28, 28);
-impl_ppi_channel!(PPI_CH29, 29);
-impl_ppi_channel!(PPI_CH30, 30);
-impl_ppi_channel!(PPI_CH31, 31);
+impl_ppi_channel!(PPI_CH0, 0, 2, 1);
+impl_ppi_channel!(PPI_CH1, 1, 2, 1);
+impl_ppi_channel!(PPI_CH2, 2, 2, 1);
+impl_ppi_channel!(PPI_CH3, 3, 2, 1);
+impl_ppi_channel!(PPI_CH4, 4, 2, 1);
+impl_ppi_channel!(PPI_CH5, 5, 2, 1);
+impl_ppi_channel!(PPI_CH6, 6, 2, 1);
+impl_ppi_channel!(PPI_CH7, 7, 2, 1);
+impl_ppi_channel!(PPI_CH8, 8, 2, 1);
+impl_ppi_channel!(PPI_CH9, 9, 2, 1);
+impl_ppi_channel!(PPI_CH10, 10, 2, 1);
+impl_ppi_channel!(PPI_CH11, 11, 2, 1);
+impl_ppi_channel!(PPI_CH12, 12, 2, 1);
+impl_ppi_channel!(PPI_CH13, 13, 2, 1);
+impl_ppi_channel!(PPI_CH14, 14, 2, 1);
+impl_ppi_channel!(PPI_CH15, 15, 2, 1);
+impl_ppi_channel!(PPI_CH16, 16, 2, 1);
+impl_ppi_channel!(PPI_CH17, 17, 2, 1);
+impl_ppi_channel!(PPI_CH18, 18, 2, 1);
+impl_ppi_channel!(PPI_CH19, 19, 2, 1);
+impl_ppi_channel!(PPI_CH20, 20, 1, 0);
+impl_ppi_channel!(PPI_CH21, 21, 1, 0);
+impl_ppi_channel!(PPI_CH22, 22, 1, 0);
+impl_ppi_channel!(PPI_CH23, 23, 1, 0);
+impl_ppi_channel!(PPI_CH24, 24, 1, 0);
+impl_ppi_channel!(PPI_CH25, 25, 1, 0);
+impl_ppi_channel!(PPI_CH26, 26, 1, 0);
+impl_ppi_channel!(PPI_CH27, 27, 1, 0);
+impl_ppi_channel!(PPI_CH28, 28, 1, 0);
+impl_ppi_channel!(PPI_CH29, 29, 1, 0);
+impl_ppi_channel!(PPI_CH30, 30, 1, 0);
+impl_ppi_channel!(PPI_CH31, 31, 1, 0);
 
 impl_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs
index 4f5c2600..6f70f2a4 100644
--- a/embassy-nrf/src/chips/nrf52833.rs
+++ b/embassy-nrf/src/chips/nrf52833.rs
@@ -226,38 +226,38 @@ 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);
-impl_ppi_channel!(PPI_CH21, 21);
-impl_ppi_channel!(PPI_CH22, 22);
-impl_ppi_channel!(PPI_CH23, 23);
-impl_ppi_channel!(PPI_CH24, 24);
-impl_ppi_channel!(PPI_CH25, 25);
-impl_ppi_channel!(PPI_CH26, 26);
-impl_ppi_channel!(PPI_CH27, 27);
-impl_ppi_channel!(PPI_CH28, 28);
-impl_ppi_channel!(PPI_CH29, 29);
-impl_ppi_channel!(PPI_CH30, 30);
-impl_ppi_channel!(PPI_CH31, 31);
+impl_ppi_channel!(PPI_CH0, 0, 2, 1);
+impl_ppi_channel!(PPI_CH1, 1, 2, 1);
+impl_ppi_channel!(PPI_CH2, 2, 2, 1);
+impl_ppi_channel!(PPI_CH3, 3, 2, 1);
+impl_ppi_channel!(PPI_CH4, 4, 2, 1);
+impl_ppi_channel!(PPI_CH5, 5, 2, 1);
+impl_ppi_channel!(PPI_CH6, 6, 2, 1);
+impl_ppi_channel!(PPI_CH7, 7, 2, 1);
+impl_ppi_channel!(PPI_CH8, 8, 2, 1);
+impl_ppi_channel!(PPI_CH9, 9, 2, 1);
+impl_ppi_channel!(PPI_CH10, 10, 2, 1);
+impl_ppi_channel!(PPI_CH11, 11, 2, 1);
+impl_ppi_channel!(PPI_CH12, 12, 2, 1);
+impl_ppi_channel!(PPI_CH13, 13, 2, 1);
+impl_ppi_channel!(PPI_CH14, 14, 2, 1);
+impl_ppi_channel!(PPI_CH15, 15, 2, 1);
+impl_ppi_channel!(PPI_CH16, 16, 2, 1);
+impl_ppi_channel!(PPI_CH17, 17, 2, 1);
+impl_ppi_channel!(PPI_CH18, 18, 2, 1);
+impl_ppi_channel!(PPI_CH19, 19, 2, 1);
+impl_ppi_channel!(PPI_CH20, 20, 1, 0);
+impl_ppi_channel!(PPI_CH21, 21, 1, 0);
+impl_ppi_channel!(PPI_CH22, 22, 1, 0);
+impl_ppi_channel!(PPI_CH23, 23, 1, 0);
+impl_ppi_channel!(PPI_CH24, 24, 1, 0);
+impl_ppi_channel!(PPI_CH25, 25, 1, 0);
+impl_ppi_channel!(PPI_CH26, 26, 1, 0);
+impl_ppi_channel!(PPI_CH27, 27, 1, 0);
+impl_ppi_channel!(PPI_CH28, 28, 1, 0);
+impl_ppi_channel!(PPI_CH29, 29, 1, 0);
+impl_ppi_channel!(PPI_CH30, 30, 1, 0);
+impl_ppi_channel!(PPI_CH31, 31, 1, 0);
 
 impl_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs
index 1006acd9..1b982415 100644
--- a/embassy-nrf/src/chips/nrf52840.rs
+++ b/embassy-nrf/src/chips/nrf52840.rs
@@ -231,38 +231,38 @@ 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);
-impl_ppi_channel!(PPI_CH21, 21);
-impl_ppi_channel!(PPI_CH22, 22);
-impl_ppi_channel!(PPI_CH23, 23);
-impl_ppi_channel!(PPI_CH24, 24);
-impl_ppi_channel!(PPI_CH25, 25);
-impl_ppi_channel!(PPI_CH26, 26);
-impl_ppi_channel!(PPI_CH27, 27);
-impl_ppi_channel!(PPI_CH28, 28);
-impl_ppi_channel!(PPI_CH29, 29);
-impl_ppi_channel!(PPI_CH30, 30);
-impl_ppi_channel!(PPI_CH31, 31);
+impl_ppi_channel!(PPI_CH0, 0, 2, 1);
+impl_ppi_channel!(PPI_CH1, 1, 2, 1);
+impl_ppi_channel!(PPI_CH2, 2, 2, 1);
+impl_ppi_channel!(PPI_CH3, 3, 2, 1);
+impl_ppi_channel!(PPI_CH4, 4, 2, 1);
+impl_ppi_channel!(PPI_CH5, 5, 2, 1);
+impl_ppi_channel!(PPI_CH6, 6, 2, 1);
+impl_ppi_channel!(PPI_CH7, 7, 2, 1);
+impl_ppi_channel!(PPI_CH8, 8, 2, 1);
+impl_ppi_channel!(PPI_CH9, 9, 2, 1);
+impl_ppi_channel!(PPI_CH10, 10, 2, 1);
+impl_ppi_channel!(PPI_CH11, 11, 2, 1);
+impl_ppi_channel!(PPI_CH12, 12, 2, 1);
+impl_ppi_channel!(PPI_CH13, 13, 2, 1);
+impl_ppi_channel!(PPI_CH14, 14, 2, 1);
+impl_ppi_channel!(PPI_CH15, 15, 2, 1);
+impl_ppi_channel!(PPI_CH16, 16, 2, 1);
+impl_ppi_channel!(PPI_CH17, 17, 2, 1);
+impl_ppi_channel!(PPI_CH18, 18, 2, 1);
+impl_ppi_channel!(PPI_CH19, 19, 2, 1);
+impl_ppi_channel!(PPI_CH20, 20, 1, 0);
+impl_ppi_channel!(PPI_CH21, 21, 1, 0);
+impl_ppi_channel!(PPI_CH22, 22, 1, 0);
+impl_ppi_channel!(PPI_CH23, 23, 1, 0);
+impl_ppi_channel!(PPI_CH24, 24, 1, 0);
+impl_ppi_channel!(PPI_CH25, 25, 1, 0);
+impl_ppi_channel!(PPI_CH26, 26, 1, 0);
+impl_ppi_channel!(PPI_CH27, 27, 1, 0);
+impl_ppi_channel!(PPI_CH28, 28, 1, 0);
+impl_ppi_channel!(PPI_CH29, 29, 1, 0);
+impl_ppi_channel!(PPI_CH30, 30, 1, 0);
+impl_ppi_channel!(PPI_CH31, 31, 1, 0);
 
 impl_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs
index 7ec072ac..943ff530 100644
--- a/embassy-nrf/src/gpiote.rs
+++ b/embassy-nrf/src/gpiote.rs
@@ -190,7 +190,13 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
     /// Returns the IN event, for use with PPI.
     pub fn event_in(&self) -> Event {
         let g = unsafe { &*pac::GPIOTE::ptr() };
-        Event::from_reg(&g.events_in[self.ch.number()])
+
+        #[cfg(feature = "_ppi")]
+        let reg = &g.events_in[self.ch.number()];
+        #[cfg(feature = "_dppi")]
+        let reg = &g.publish_in[self.ch.number()];
+
+        Event::from_reg(reg)
     }
 }
 
@@ -271,21 +277,33 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
     /// Returns the OUT task, for use with PPI.
     pub fn task_out(&self) -> Task {
         let g = unsafe { &*pac::GPIOTE::ptr() };
-        Task::from_reg(&g.tasks_out[self.ch.number()])
+        #[cfg(feature = "_ppi")]
+        let reg = &g.tasks_out[self.ch.number()];
+        #[cfg(feature = "_dppi")]
+        let reg = &g.subscribe_out[self.ch.number()];
+        Task::from_reg(reg)
     }
 
     /// Returns the CLR task, for use with PPI.
     #[cfg(not(feature = "nrf51"))]
     pub fn task_clr(&self) -> Task {
         let g = unsafe { &*pac::GPIOTE::ptr() };
-        Task::from_reg(&g.tasks_clr[self.ch.number()])
+        #[cfg(feature = "_ppi")]
+        let reg = &g.tasks_clr[self.ch.number()];
+        #[cfg(feature = "_dppi")]
+        let reg = &g.subscribe_clr[self.ch.number()];
+        Task::from_reg(reg)
     }
 
     /// Returns the SET task, for use with PPI.
     #[cfg(not(feature = "nrf51"))]
     pub fn task_set(&self) -> Task {
         let g = unsafe { &*pac::GPIOTE::ptr() };
-        Task::from_reg(&g.tasks_set[self.ch.number()])
+        #[cfg(feature = "_ppi")]
+        let reg = &g.tasks_set[self.ch.number()];
+        #[cfg(feature = "_dppi")]
+        let reg = &g.subscribe_set[self.ch.number()];
+        Task::from_reg(reg)
     }
 }
 
diff --git a/embassy-nrf/src/ppi.rs b/embassy-nrf/src/ppi.rs
index f5e06c69..822663ea 100644
--- a/embassy-nrf/src/ppi.rs
+++ b/embassy-nrf/src/ppi.rs
@@ -20,6 +20,16 @@ use embassy_hal_common::{unborrow, unsafe_impl_unborrow};
 // ======================
 //       driver
 
+#[non_exhaustive]
+#[derive(Clone, Debug)]
+pub enum Error {
+    NoCapacityLeft,
+    UnknownTask,
+    TaskAlreadyInUse,
+    UnknownEvent,
+    EventAlreadyInUse,
+}
+
 pub struct Ppi<'d, C: Channel> {
     ch: C,
     phantom: PhantomData<&'d mut C>,
@@ -29,14 +39,10 @@ impl<'d, C: Channel> Ppi<'d, C> {
     pub fn new(ch: impl Unborrow<Target = C> + 'd) -> Self {
         unborrow!(ch);
 
-        #[allow(unused_mut)]
-        let mut this = Self {
+        Self {
             ch,
             phantom: PhantomData,
-        };
-        #[cfg(not(any(feature = "nrf51", feature = "nrf9160")))]
-        this.clear_fork_task();
-        this
+        }
     }
 
     /// Enables the channel.
@@ -52,35 +58,279 @@ impl<'d, C: Channel> Ppi<'d, C> {
         r.chenclr
             .write(|w| unsafe { w.bits(1 << self.ch.number()) });
     }
+}
 
-    #[cfg(not(any(feature = "nrf51", feature = "nrf9160")))]
-    /// Sets the fork task that must be triggered when the configured event occurs. The user must
-    /// provide a reference to the task.
-    pub fn set_fork_task(&mut self, task: Task) {
+#[cfg(feature = "_ppi")]
+impl<'d, C: Channel> Ppi<'d, C> {
+    /// Makes it so that the given task is subscribed to this channel
+    pub fn subscribe(&mut self, task: Task) -> Result<(), Error> {
+        if self.is_main_task_free() {
+            self.set_main_task(Some(task));
+            Ok(())
+        } else if self.is_fork_task_free() {
+            self.set_fork_task(Some(task));
+            Ok(())
+        } else {
+            Err(Error::NoCapacityLeft)
+        }
+    }
+
+    /// Makes it so that the given task is not subscribed to this channel
+    pub fn unsubscribe(&mut self, task: Task) -> Result<(), Error> {
+        if self.get_main_task() == Some(task) {
+            // If there is a fork task, we move that to the main task for consistency
+            // If there is no fork task, then the main task is set to 0
+            let fork_task = self.get_fork_task();
+            self.set_main_task(fork_task);
+
+            if self.has_fork_task() {
+                // The fork task was copied to the main task, so reset the fork task
+                self.set_fork_task(None);
+            }
+            Ok(())
+        } else if self.get_fork_task() == Some(task) {
+            // Reset the fork task
+            self.set_fork_task(None);
+            Ok(())
+        } else {
+            Err(Error::UnknownTask)
+        }
+    }
+
+    /// Makes it so that the given event is published on this channel
+    pub fn publish(&mut self, event: Event) -> Result<(), Error> {
+        if self.is_event_free() {
+            self.set_event(Some(event));
+            Ok(())
+        } else {
+            Err(Error::NoCapacityLeft)
+        }
+    }
+
+    /// Makes it so that the given event is not published on this channel
+    pub fn unpublish(&mut self, event: Event) -> Result<(), Error> {
+        if self.get_event() == Some(event) {
+            self.set_event(None);
+            Ok(())
+        } else {
+            Err(Error::UnknownEvent)
+        }
+    }
+
+    fn set_main_task(&mut self, task: Option<Task>) {
         let r = unsafe { &*pac::PPI::ptr() };
-        r.fork[self.ch.number()]
-            .tep
-            .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
+        if let Some(task) = task {
+            r.ch[self.ch.number()]
+                .tep
+                .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
+        } else {
+            r.ch[self.ch.number()].tep.write(|w| unsafe { w.bits(0) })
+        }
     }
 
-    #[cfg(not(any(feature = "nrf51", feature = "nrf9160")))]
-    /// Clear the fork task endpoint. Previously set task will no longer be triggered.
-    pub fn clear_fork_task(&mut self) {
+    fn get_main_task(&mut self) -> Option<Task> {
         let r = unsafe { &*pac::PPI::ptr() };
-        r.fork[self.ch.number()].tep.write(|w| unsafe { w.bits(0) })
+
+        if !self.has_main_task() {
+            return None;
+        }
+
+        let bits = r.ch[self.ch.number()].tep.read().tep().bits();
+
+        if bits == 0 {
+            None
+        } else {
+            unsafe { Some(Task(NonNull::new_unchecked(bits as *mut _))) }
+        }
     }
 
-    #[cfg(feature = "nrf9160")]
-    /// Sets the fork task that must be triggered when the configured event occurs. The user must
-    /// provide a reference to the task.
-    pub fn set_fork_task(&mut self, _task: Task) {
-        todo!("Tasks not yet implemented for nrf9160");
+    fn set_fork_task(&mut self, task: Option<Task>) {
+        let r = unsafe { &*pac::PPI::ptr() };
+        if let Some(task) = task {
+            r.fork[self.ch.number()]
+                .tep
+                .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
+        } else {
+            r.fork[self.ch.number()].tep.write(|w| unsafe { w.bits(0) })
+        }
     }
 
-    #[cfg(feature = "nrf9160")]
-    /// Clear the fork task endpoint. Previously set task will no longer be triggered.
-    pub fn clear_fork_task(&mut self) {
-        todo!("Tasks not yet implemented for nrf9160");
+    fn get_fork_task(&mut self) -> Option<Task> {
+        let r = unsafe { &*pac::PPI::ptr() };
+
+        if !self.has_fork_task() {
+            return None;
+        }
+
+        let bits = r.fork[self.ch.number()].tep.read().tep().bits();
+
+        if bits == 0 {
+            None
+        } else {
+            unsafe { Some(Task(NonNull::new_unchecked(bits as *mut _))) }
+        }
+    }
+
+    fn has_main_task(&self) -> bool {
+        match (self.ch.task_capacity(), self.ch.event_capacity()) {
+            (0, 0) => false,     // Static task
+            (1, 0) => false,     // Static task with fork
+            (1 | 2, 1) => true,  // Configurable task with possibly a fork
+            _ => unreachable!(), // Every PPI config is covered
+        }
+    }
+
+    fn has_fork_task(&self) -> bool {
+        match (self.ch.task_capacity(), self.ch.event_capacity()) {
+            (0, 0) => false,     // Static task
+            (1, 0) => true,      // Static task with fork
+            (1, 1) => false,     // Configurable task without fork
+            (2, 1) => true,      // Configurable task with fork
+            _ => unreachable!(), // Every PPI config is covered
+        }
+    }
+
+    fn is_main_task_free(&mut self) -> bool {
+        self.get_main_task().is_none()
+    }
+
+    fn is_fork_task_free(&mut self) -> bool {
+        self.get_fork_task().is_none()
+    }
+
+    fn set_event(&mut self, event: Option<Event>) {
+        let r = unsafe { &*pac::PPI::ptr() };
+        if let Some(event) = event {
+            r.ch[self.ch.number()]
+                .eep
+                .write(|w| unsafe { w.bits(event.0.as_ptr() as u32) })
+        } else {
+            r.ch[self.ch.number()].eep.write(|w| unsafe { w.bits(0) })
+        }
+    }
+
+    fn get_event(&mut self) -> Option<Event> {
+        let r = unsafe { &*pac::PPI::ptr() };
+
+        if !self.has_event() {
+            return None;
+        }
+
+        let bits = r.ch[self.ch.number()].eep.read().eep().bits();
+
+        if bits == 0 {
+            None
+        } else {
+            unsafe { Some(Event(NonNull::new_unchecked(bits as *mut _))) }
+        }
+    }
+
+    fn has_event(&self) -> bool {
+        match (self.ch.task_capacity(), self.ch.event_capacity()) {
+            (_, 0) => false,     // Static event
+            (_, 1) => true,      // Configurable event
+            _ => unreachable!(), // Every PPI config is covered
+        }
+    }
+
+    fn is_event_free(&mut self) -> bool {
+        self.get_event().is_none()
+    }
+}
+
+#[cfg(feature = "_dppi")]
+const DPPI_ENABLE_BIT: u32 = 0x8000_0000;
+#[cfg(feature = "_dppi")]
+const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF;
+
+#[cfg(feature = "_dppi")]
+impl<'d, C: Channel> Ppi<'d, C> {
+    /// Makes it so that the given task is subscribed to this channel
+    pub fn subscribe(&mut self, task: Task) -> Result<(), Error> {
+        unsafe {
+            if Self::is_register_enabled(task.0) {
+                Err(Error::TaskAlreadyInUse)
+            } else {
+                Self::set_register_active(task.0, self.ch.number() as u8);
+                Ok(())
+            }
+        }
+    }
+
+    /// Makes it so that the given task is not subscribed to this channel
+    pub fn unsubscribe(&mut self, task: Task) -> Result<(), Error> {
+        unsafe {
+            if Self::get_register_channel(task.0) != self.ch.number() as u8 {
+                Err(Error::UnknownTask)
+            } else {
+                Self::set_register_inactive(task.0);
+                Ok(())
+            }
+        }
+    }
+
+    /// Makes it so that the given event is published on this channel
+    pub fn publish(&mut self, event: Event) -> Result<(), Error> {
+        unsafe {
+            if Self::is_register_enabled(event.0) {
+                Err(Error::TaskAlreadyInUse)
+            } else {
+                Self::set_register_active(event.0, self.ch.number() as u8);
+                Ok(())
+            }
+        }
+    }
+
+    /// Makes it so that the given event is not published on this channel
+    pub fn unpublish(&mut self, event: Event) -> Result<(), Error> {
+        unsafe {
+            if Self::get_register_channel(event.0) != self.ch.number() as u8 {
+                Err(Error::UnknownTask)
+            } else {
+                Self::set_register_inactive(event.0);
+                Ok(())
+            }
+        }
+    }
+
+    /// Checks if the DPPI_ENABLE_BIT is set in the register
+    ///
+    /// # Safety
+    ///
+    /// The register pointer must point at one of the many SUBSCRIBE_* or PUBLISH_* registers of the peripherals
+    unsafe fn is_register_enabled(register: NonNull<u32>) -> bool {
+        let bits = register.as_ptr().read_volatile();
+        bits & DPPI_ENABLE_BIT > 0
+    }
+
+    /// Sets the register to the given channel and enables it
+    ///
+    /// # Safety
+    ///
+    /// The register pointer must point at one of the many SUBSCRIBE_* or PUBLISH_* registers of the peripherals
+    unsafe fn set_register_active(register: NonNull<u32>, channel: u8) {
+        register
+            .as_ptr()
+            .write_volatile(DPPI_ENABLE_BIT | (channel as u32 & DPPI_CHANNEL_MASK));
+    }
+
+    /// Resets the channel number and disables the register
+    ///
+    /// # Safety
+    ///
+    /// The register pointer must point at one of the many SUBSCRIBE_* or PUBLISH_* registers of the peripherals
+    unsafe fn set_register_inactive(register: NonNull<u32>) {
+        register.as_ptr().write_volatile(0);
+    }
+
+    /// Gets the current configured channel number of the register
+    ///
+    /// # Safety
+    ///
+    /// The register pointer must point at one of the many SUBSCRIBE_* or PUBLISH_* registers of the peripherals
+    unsafe fn get_register_channel(register: NonNull<u32>) -> u8 {
+        let bits = register.as_ptr().read_volatile();
+        (bits & DPPI_CHANNEL_MASK) as u8
     }
 }
 
@@ -90,73 +340,59 @@ impl<'d, C: Channel> Drop for Ppi<'d, C> {
     }
 }
 
-#[cfg(not(feature = "nrf9160"))]
-impl<'d, C: ConfigurableChannel> Ppi<'d, C> {
-    /// Sets the task to be triggered when the configured event occurs.
-    pub fn set_task(&mut self, task: Task) {
-        let r = unsafe { &*pac::PPI::ptr() };
-        r.ch[self.ch.number()]
-            .tep
-            .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
-    }
-
-    /// Sets the event that will trigger the chosen task(s).
-    pub fn set_event(&mut self, event: Event) {
-        let r = unsafe { &*pac::PPI::ptr() };
-        r.ch[self.ch.number()]
-            .eep
-            .write(|w| unsafe { w.bits(event.0.as_ptr() as u32) })
+/// Represents a task that a peripheral can do.
+/// When a task is subscribed to a PPI channel it will run when the channel is triggered by
+/// a published event.
+///
+/// The pointer in the task can point to two different kinds of register:
+/// - PPI *(nRF51 & nRF52)*: A pointer to a task register of the task of the peripheral that has
+/// to be registered with the PPI to subscribe to a channel
+/// - DPPI *(nRF53 & nRF91)*: A pointer to the subscribe register of the task of the peripheral
+/// that has to have the channel number and enable bit written tp it to subscribe to a channel
+#[derive(PartialEq, Eq, Clone, Copy)]
+pub struct Task(pub NonNull<u32>);
+impl Task {
+    pub(crate) fn from_reg<T>(reg: &T) -> Self {
+        Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
     }
 }
 
-#[cfg(feature = "nrf9160")]
-impl<'d, C: ConfigurableChannel> Ppi<'d, C> {
-    /// Sets the task to be triggered when the configured event occurs.
-    pub fn set_task(&mut self, _task: Task) {
-        todo!("Tasks not yet implemented for nrf9160")
-    }
-
-    /// Sets the event that will trigger the chosen task(s).
-    pub fn set_event(&mut self, _event: Event) {
-        todo!("Events not yet implemented for nrf9160")
+/// Represents an event that a peripheral can publish.
+/// An event can be set to publish on a PPI channel when the event happens.
+///
+/// The pointer in the event can point to two different kinds of register:
+/// - PPI *(nRF51 & nRF52)*: A pointer to an event register of the event of the peripheral that has
+/// to be registered with the PPI to publish to a channel
+/// - DPPI *(nRF53 & nRF91)*: A pointer to the publish register of the event of the peripheral
+/// that has to have the channel number and enable bit written tp it to publish to a channel
+#[derive(PartialEq, Eq, Clone, Copy)]
+pub struct Event(pub NonNull<u32>);
+impl Event {
+    pub(crate) fn from_reg<T>(reg: &T) -> Self {
+        Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
     }
 }
 
 // ======================
 //       traits
 
-pub struct Task(pub NonNull<()>);
-impl Task {
-    pub(crate) fn from_reg<T>(reg: &T) -> Self {
-        Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut ()) })
-    }
-}
-
-pub struct Event(pub NonNull<()>);
-impl Event {
-    pub(crate) fn from_reg<T>(reg: &T) -> Self {
-        Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut ()) })
-    }
-}
-
 pub(crate) mod sealed {
-    pub trait ConfigurableChannel {}
     pub trait Channel {}
     pub trait Group {}
 }
 
 pub trait Channel: sealed::Channel + Sized {
     fn number(&self) -> usize;
+    fn task_capacity(&self) -> usize;
+    fn event_capacity(&self) -> usize;
+
     fn degrade(self) -> AnyChannel {
+        pub trait ConfigurableChannel {}
+
         AnyChannel {
             number: self.number() as u8,
-        }
-    }
-}
-pub trait ConfigurableChannel: Channel + sealed::ConfigurableChannel {
-    fn degrade_configurable(self) -> AnyConfigurableChannel {
-        AnyConfigurableChannel {
-            number: self.number() as u8,
+            task_capacity: self.task_capacity() as _,
+            event_capacity: self.event_capacity() as _,
         }
     }
 }
@@ -175,6 +411,8 @@ pub trait Group: sealed::Group + Sized {
 
 pub struct AnyChannel {
     number: u8,
+    task_capacity: u8,
+    event_capacity: u8,
 }
 unsafe_impl_unborrow!(AnyChannel);
 impl sealed::Channel for AnyChannel {}
@@ -182,26 +420,30 @@ impl Channel for AnyChannel {
     fn number(&self) -> usize {
         self.number as usize
     }
-}
 
-pub struct AnyConfigurableChannel {
-    number: u8,
-}
-unsafe_impl_unborrow!(AnyConfigurableChannel);
-impl sealed::Channel for AnyConfigurableChannel {}
-impl sealed::ConfigurableChannel for AnyConfigurableChannel {}
-impl ConfigurableChannel for AnyConfigurableChannel {}
-impl Channel for AnyConfigurableChannel {
-    fn number(&self) -> usize {
-        self.number as usize
+    fn task_capacity(&self) -> usize {
+        self.task_capacity as _
+    }
+
+    fn event_capacity(&self) -> usize {
+        self.event_capacity as _
     }
 }
 
 macro_rules! impl_ppi_channel {
-    ($type:ident, $number:expr, configurable) => {
-        impl_ppi_channel!($type, $number);
-        impl crate::ppi::sealed::ConfigurableChannel for peripherals::$type {}
-        impl crate::ppi::ConfigurableChannel for peripherals::$type {}
+    ($type:ident, $number:expr, $task_capacity:expr, $event_capacity:expr) => {
+        impl crate::ppi::sealed::Channel for peripherals::$type {}
+        impl crate::ppi::Channel for peripherals::$type {
+            fn number(&self) -> usize {
+                $number
+            }
+            fn task_capacity(&self) -> usize {
+                $task_capacity
+            }
+            fn event_capacity(&self) -> usize {
+                $event_capacity
+            }
+        }
     };
     ($type:ident, $number:expr) => {
         impl crate::ppi::sealed::Channel for peripherals::$type {}
@@ -209,6 +451,12 @@ macro_rules! impl_ppi_channel {
             fn number(&self) -> usize {
                 $number
             }
+            fn task_capacity(&self) -> usize {
+                usize::MAX
+            }
+            fn event_capacity(&self) -> usize {
+                usize::MAX
+            }
         }
     };
 }
diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs
index 5690ff0d..e62120ae 100644
--- a/embassy-nrf/src/timer.rs
+++ b/embassy-nrf/src/timer.rs
@@ -184,21 +184,36 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> {
     ///
     /// When triggered, this task starts the timer.
     pub fn task_start(&self) -> Task {
-        Task::from_reg(&T::regs().tasks_start)
+        #[cfg(feature = "_ppi")]
+        let reg = &T::regs().tasks_start;
+        #[cfg(feature = "_dppi")]
+        let reg = &T::regs().subscribe_start;
+
+        Task::from_reg(reg)
     }
 
     /// Returns the STOP task, for use with PPI.
     ///
     /// When triggered, this task stops the timer.
     pub fn task_stop(&self) -> Task {
-        Task::from_reg(&T::regs().tasks_stop)
+        #[cfg(feature = "_ppi")]
+        let reg = &T::regs().tasks_stop;
+        #[cfg(feature = "_dppi")]
+        let reg = &T::regs().subscribe_stop;
+
+        Task::from_reg(reg)
     }
 
     /// Returns the CLEAR task, for use with PPI.
     ///
     /// When triggered, this task resets the timer's counter to 0.
     pub fn task_clear(&self) -> Task {
-        Task::from_reg(&T::regs().tasks_clear)
+        #[cfg(feature = "_ppi")]
+        let reg = &T::regs().tasks_clear;
+        #[cfg(feature = "_dppi")]
+        let reg = &T::regs().subscribe_clear;
+
+        Task::from_reg(reg)
     }
 
     /// Change the timer's frequency.
@@ -319,14 +334,24 @@ impl<'a, T: Instance, I: TimerType> Cc<'a, T, I> {
     ///
     /// When triggered, this task will capture the current value of the timer's counter in this register.
     pub fn task_capture(&self) -> Task {
-        Task::from_reg(&T::regs().tasks_capture[self.n])
+        #[cfg(feature = "_ppi")]
+        let reg = &T::regs().tasks_capture;
+        #[cfg(feature = "_dppi")]
+        let reg = &T::regs().subscribe_capture;
+
+        Task::from_reg(reg)
     }
 
     /// Returns this CC register's COMPARE event, for use with PPI.
     ///
     /// This event will fire when the timer's counter reaches the value in this CC register.
     pub fn event_compare(&self) -> Event {
-        Event::from_reg(&T::regs().events_compare[self.n])
+        #[cfg(feature = "_ppi")]
+        let reg = &T::regs().events_compare[self.n];
+        #[cfg(feature = "_dppi")]
+        let reg = &T::regs().publish_compare[self.n];
+
+        Event::from_reg(reg)
     }
 
     /// Enable the shortcut between this CC register's COMPARE event and the timer's CLEAR task.
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs
index 63bbe5a7..f2bf01f1 100644
--- a/embassy-nrf/src/uarte.rs
+++ b/embassy-nrf/src/uarte.rs
@@ -7,7 +7,7 @@ use core::marker::PhantomData;
 use core::sync::atomic::{compiler_fence, Ordering};
 use core::task::Poll;
 use embassy::interrupt::InterruptExt;
-use embassy::traits::uart::{Error, Read, ReadUntilIdle, Write};
+use embassy::traits::uart::{Error as TraitError, Read, ReadUntilIdle, Write};
 use embassy::util::Unborrow;
 use embassy_hal_common::drop::OnDrop;
 use embassy_hal_common::unborrow;
@@ -18,13 +18,25 @@ use crate::gpio::sealed::Pin as _;
 use crate::gpio::{self, OptionalPin as GpioOptionalPin, Pin as GpioPin};
 use crate::interrupt::Interrupt;
 use crate::pac;
-use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};
+use crate::ppi::{AnyChannel, Channel, Event, Ppi, Task};
 use crate::timer::Instance as TimerInstance;
 use crate::timer::{Frequency, Timer};
 
 // Re-export SVD variants to allow user to directly set values.
 pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
 
+#[non_exhaustive]
+#[derive(Clone, Debug)]
+pub enum Error {
+    PpiError(crate::ppi::Error),
+}
+
+impl From<crate::ppi::Error> for Error {
+    fn from(e: crate::ppi::Error) -> Self {
+        Self::PpiError(e)
+    }
+}
+
 #[non_exhaustive]
 pub struct Config {
     pub parity: Parity,
@@ -219,7 +231,7 @@ impl<'a, T: Instance> Drop for Uarte<'a, T> {
 
 impl<'d, T: Instance> Read for Uarte<'d, T> {
     #[rustfmt::skip]
-    type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a;
+    type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), TraitError>> + 'a;
 
     fn read<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
         async move {
@@ -273,7 +285,7 @@ impl<'d, T: Instance> Read for Uarte<'d, T> {
 
 impl<'d, T: Instance> Write for Uarte<'d, T> {
     #[rustfmt::skip]
-    type WriteFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a;
+    type WriteFuture<'a> where Self: 'a = impl Future<Output = Result<(), TraitError>> + 'a;
 
     fn write<'a>(&'a mut self, tx_buffer: &'a [u8]) -> Self::WriteFuture<'a> {
         async move {
@@ -331,14 +343,17 @@ impl<'d, T: Instance> Write for Uarte<'d, T> {
 pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> {
     uarte: Uarte<'d, U>,
     timer: Timer<'d, T>,
-    ppi_ch1: Ppi<'d, AnyConfigurableChannel>,
-    _ppi_ch2: Ppi<'d, AnyConfigurableChannel>,
+    ppi_ch1: Ppi<'d, AnyChannel>,
+    _ppi_ch2: Ppi<'d, AnyChannel>,
 }
 
 impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
     /// Creates the interface to a UARTE instance.
     /// Sets the baud rate, parity and assigns the pins to the UARTE peripheral.
     ///
+    /// - *Note:* ppi_ch1 must have at least 1 free event and 2 free tasks or a PPI error is returned
+    /// - *Note:* ppi_ch2 must have at least 1 free event and 1 free tasks or a PPI error is returned
+    ///
     /// # Safety
     ///
     /// The returned API is safe unless you use `mem::forget` (or similar safe mechanisms)
@@ -348,15 +363,15 @@ impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
     pub unsafe fn new(
         uarte: impl Unborrow<Target = U> + 'd,
         timer: impl Unborrow<Target = T> + 'd,
-        ppi_ch1: impl Unborrow<Target = impl ConfigurableChannel> + 'd,
-        ppi_ch2: impl Unborrow<Target = impl ConfigurableChannel> + 'd,
+        ppi_ch1: impl Unborrow<Target = impl Channel> + 'd,
+        ppi_ch2: impl Unborrow<Target = impl Channel> + 'd,
         irq: impl Unborrow<Target = U::Interrupt> + 'd,
         rxd: impl Unborrow<Target = impl GpioPin> + 'd,
         txd: impl Unborrow<Target = impl GpioPin> + 'd,
         cts: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
         rts: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
         config: Config,
-    ) -> Self {
+    ) -> Result<Self, Error> {
         let baudrate = config.baudrate;
         let uarte = Uarte::new(uarte, irq, rxd, txd, cts, rts, config);
         let mut timer = Timer::new(timer);
@@ -378,29 +393,29 @@ impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
         timer.cc(0).short_compare_clear();
         timer.cc(0).short_compare_stop();
 
-        let mut ppi_ch1 = Ppi::new(ppi_ch1.degrade_configurable());
-        ppi_ch1.set_event(Event::from_reg(&r.events_rxdrdy));
-        ppi_ch1.set_task(timer.task_clear());
-        ppi_ch1.set_fork_task(timer.task_start());
+        let mut ppi_ch1 = Ppi::new(ppi_ch1.degrade());
+        ppi_ch1.publish(Event::from_reg(&r.events_rxdrdy))?;
+        ppi_ch1.subscribe(timer.task_clear())?;
+        ppi_ch1.subscribe(timer.task_start())?;
         ppi_ch1.enable();
 
-        let mut ppi_ch2 = Ppi::new(ppi_ch2.degrade_configurable());
-        ppi_ch2.set_event(timer.cc(0).event_compare());
-        ppi_ch2.set_task(Task::from_reg(&r.tasks_stoprx));
+        let mut ppi_ch2 = Ppi::new(ppi_ch2.degrade());
+        ppi_ch2.publish(timer.cc(0).event_compare())?;
+        ppi_ch2.subscribe(Task::from_reg(&r.tasks_stoprx))?;
         ppi_ch2.enable();
 
-        Self {
+        Ok(Self {
             uarte,
             timer,
             ppi_ch1: ppi_ch1,
             _ppi_ch2: ppi_ch2,
-        }
+        })
     }
 }
 
 impl<'d, U: Instance, T: TimerInstance> ReadUntilIdle for UarteWithIdle<'d, U, T> {
     #[rustfmt::skip]
-    type ReadUntilIdleFuture<'a> where Self: 'a = impl Future<Output = Result<usize, Error>> + 'a;
+    type ReadUntilIdleFuture<'a> where Self: 'a = impl Future<Output = Result<usize, TraitError>> + 'a;
     fn read_until_idle<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> Self::ReadUntilIdleFuture<'a> {
         async move {
             let ptr = rx_buffer.as_ptr();
@@ -460,7 +475,7 @@ impl<'d, U: Instance, T: TimerInstance> ReadUntilIdle for UarteWithIdle<'d, U, T
 
 impl<'d, U: Instance, T: TimerInstance> Read for UarteWithIdle<'d, U, T> {
     #[rustfmt::skip]
-    type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a;
+    type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), TraitError>> + 'a;
     fn read<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
         async move {
             self.ppi_ch1.disable();
@@ -473,7 +488,7 @@ impl<'d, U: Instance, T: TimerInstance> Read for UarteWithIdle<'d, U, T> {
 
 impl<'d, U: Instance, T: TimerInstance> Write for UarteWithIdle<'d, U, T> {
     #[rustfmt::skip]
-    type WriteFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a;
+    type WriteFuture<'a> where Self: 'a = impl Future<Output = Result<(), TraitError>> + 'a;
 
     fn write<'a>(&'a mut self, tx_buffer: &'a [u8]) -> Self::WriteFuture<'a> {
         self.uarte.write(tx_buffer)
diff --git a/examples/nrf/src/bin/ppi.rs b/examples/nrf/src/bin/ppi.rs
index 6ea03ebd..55089396 100644
--- a/examples/nrf/src/bin/ppi.rs
+++ b/examples/nrf/src/bin/ppi.rs
@@ -52,24 +52,24 @@ async fn main(_spawner: Spawner, p: Peripherals) {
     );
 
     let mut ppi = Ppi::new(p.PPI_CH0);
-    ppi.set_event(button1.event_in());
-    ppi.set_task(led1.task_out());
+    ppi.publish(button1.event_in()).unwrap();
+    ppi.subscribe(led1.task_out()).unwrap();
     ppi.enable();
 
     let mut ppi = Ppi::new(p.PPI_CH1);
-    ppi.set_event(button2.event_in());
-    ppi.set_task(led1.task_clr());
+    ppi.publish(button2.event_in()).unwrap();
+    ppi.subscribe(led1.task_clr()).unwrap();
     ppi.enable();
 
     let mut ppi = Ppi::new(p.PPI_CH2);
-    ppi.set_event(button3.event_in());
-    ppi.set_task(led1.task_set());
+    ppi.publish(button3.event_in()).unwrap();
+    ppi.subscribe(led1.task_set()).unwrap();
     ppi.enable();
 
     let mut ppi = Ppi::new(p.PPI_CH3);
-    ppi.set_event(button4.event_in());
-    ppi.set_task(led1.task_out());
-    ppi.set_fork_task(led2.task_out());
+    ppi.publish(button4.event_in()).unwrap();
+    ppi.subscribe(led1.task_out()).unwrap();
+    ppi.subscribe(led2.task_out()).unwrap();
     ppi.enable();
 
     info!("PPI setup!");

From 4950682a50c8561124462cd7d774a72626271294 Mon Sep 17 00:00:00 2001
From: Dion Dokter <dion@tweedegolf.com>
Date: Thu, 14 Oct 2021 14:45:57 +0200
Subject: [PATCH 02/10] Some extra docs and better naming

---
 embassy-nrf/src/ppi.rs | 36 ++++++++++++++++++++++++++----------
 1 file changed, 26 insertions(+), 10 deletions(-)

diff --git a/embassy-nrf/src/ppi.rs b/embassy-nrf/src/ppi.rs
index 822663ea..1f825bf4 100644
--- a/embassy-nrf/src/ppi.rs
+++ b/embassy-nrf/src/ppi.rs
@@ -20,14 +20,16 @@ use embassy_hal_common::{unborrow, unsafe_impl_unborrow};
 // ======================
 //       driver
 
+/// Error type of the PPI driver
 #[non_exhaustive]
 #[derive(Clone, Debug)]
 pub enum Error {
+    /// There is no capacity to enable this task or event (nRF51 & nRF52 only)
     NoCapacityLeft,
-    UnknownTask,
-    TaskAlreadyInUse,
-    UnknownEvent,
-    EventAlreadyInUse,
+    /// This task or event is not in use by the current channel
+    NotInUseByChannel,
+    /// This task or event is already enabled on another channel (nRF53 & nRF91 only)
+    AlreadyInUse,
 }
 
 pub struct Ppi<'d, C: Channel> {
@@ -93,7 +95,7 @@ impl<'d, C: Channel> Ppi<'d, C> {
             self.set_fork_task(None);
             Ok(())
         } else {
-            Err(Error::UnknownTask)
+            Err(Error::NotInUseByChannel)
         }
     }
 
@@ -113,7 +115,7 @@ impl<'d, C: Channel> Ppi<'d, C> {
             self.set_event(None);
             Ok(())
         } else {
-            Err(Error::UnknownEvent)
+            Err(Error::NotInUseByChannel)
         }
     }
 
@@ -249,7 +251,7 @@ impl<'d, C: Channel> Ppi<'d, C> {
     pub fn subscribe(&mut self, task: Task) -> Result<(), Error> {
         unsafe {
             if Self::is_register_enabled(task.0) {
-                Err(Error::TaskAlreadyInUse)
+                Err(Error::AlreadyInUse)
             } else {
                 Self::set_register_active(task.0, self.ch.number() as u8);
                 Ok(())
@@ -261,7 +263,7 @@ impl<'d, C: Channel> Ppi<'d, C> {
     pub fn unsubscribe(&mut self, task: Task) -> Result<(), Error> {
         unsafe {
             if Self::get_register_channel(task.0) != self.ch.number() as u8 {
-                Err(Error::UnknownTask)
+                Err(Error::NotInUseByChannel)
             } else {
                 Self::set_register_inactive(task.0);
                 Ok(())
@@ -273,7 +275,7 @@ impl<'d, C: Channel> Ppi<'d, C> {
     pub fn publish(&mut self, event: Event) -> Result<(), Error> {
         unsafe {
             if Self::is_register_enabled(event.0) {
-                Err(Error::TaskAlreadyInUse)
+                Err(Error::AlreadyInUse)
             } else {
                 Self::set_register_active(event.0, self.ch.number() as u8);
                 Ok(())
@@ -285,7 +287,7 @@ impl<'d, C: Channel> Ppi<'d, C> {
     pub fn unpublish(&mut self, event: Event) -> Result<(), Error> {
         unsafe {
             if Self::get_register_channel(event.0) != self.ch.number() as u8 {
-                Err(Error::UnknownTask)
+                Err(Error::NotInUseByChannel)
             } else {
                 Self::set_register_inactive(event.0);
                 Ok(())
@@ -382,8 +384,22 @@ pub(crate) mod sealed {
 }
 
 pub trait Channel: sealed::Channel + Sized {
+    /// Returns the number of the channel
     fn number(&self) -> usize;
+
+    /// Returns the amount of configurable tasks this channel has.
+    ///
+    /// - MAX for DPPI with unlimited capacity (nRF53 & nRF91)
+    /// - 0 for static channel without fork (nRF51)
+    /// - 1 for static channel with fork (nRF52) or for configurable channel (nRF51)
+    /// - 2 for configurable channel with fork (nRF52)
     fn task_capacity(&self) -> usize;
+
+    /// Returns the amount of configurable events this channels has
+    ///
+    /// - MAX for DPPI with unlimited capacity (nRF53 & nRF91)
+    /// - 0 for static channel (nRF51 & nRF52)
+    /// - 1 for configurable channel (nRF51 & nRF52)
     fn event_capacity(&self) -> usize;
 
     fn degrade(self) -> AnyChannel {

From e6ec81b999541cca847b50075ac0d5d826f97dcd Mon Sep 17 00:00:00 2001
From: Dion Dokter <dion@tweedegolf.com>
Date: Thu, 14 Oct 2021 15:13:45 +0200
Subject: [PATCH 03/10] Fixed examples and added defmt format to the new error
 types

---
 embassy-nrf/src/buffered_uarte.rs     | 1 +
 embassy-nrf/src/ppi.rs                | 1 +
 embassy-nrf/src/uarte.rs              | 1 +
 examples/nrf/src/bin/buffered_uart.rs | 4 ++--
 examples/nrf/src/bin/uart_idle.rs     | 4 ++--
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs
index f7e55e42..8ec0fb2c 100644
--- a/embassy-nrf/src/buffered_uarte.rs
+++ b/embassy-nrf/src/buffered_uarte.rs
@@ -25,6 +25,7 @@ pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Pari
 
 #[non_exhaustive]
 #[derive(Clone, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum Error {
     PpiError(ppi::Error),
 }
diff --git a/embassy-nrf/src/ppi.rs b/embassy-nrf/src/ppi.rs
index 1f825bf4..7aaf17ee 100644
--- a/embassy-nrf/src/ppi.rs
+++ b/embassy-nrf/src/ppi.rs
@@ -23,6 +23,7 @@ use embassy_hal_common::{unborrow, unsafe_impl_unborrow};
 /// Error type of the PPI driver
 #[non_exhaustive]
 #[derive(Clone, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum Error {
     /// There is no capacity to enable this task or event (nRF51 & nRF52 only)
     NoCapacityLeft,
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs
index f2bf01f1..71401a56 100644
--- a/embassy-nrf/src/uarte.rs
+++ b/embassy-nrf/src/uarte.rs
@@ -27,6 +27,7 @@ pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Pari
 
 #[non_exhaustive]
 #[derive(Clone, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum Error {
     PpiError(crate::ppi::Error),
 }
diff --git a/examples/nrf/src/bin/buffered_uart.rs b/examples/nrf/src/bin/buffered_uart.rs
index 69c7de93..b01c83ce 100644
--- a/examples/nrf/src/bin/buffered_uart.rs
+++ b/examples/nrf/src/bin/buffered_uart.rs
@@ -26,7 +26,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
     let irq = interrupt::take!(UARTE0_UART0);
     let mut state = State::new();
     let u = unsafe {
-        BufferedUarte::new(
+        unwrap!(BufferedUarte::new(
             &mut state,
             p.UARTE0,
             p.TIMER0,
@@ -40,7 +40,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
             config,
             &mut rx_buffer,
             &mut tx_buffer,
-        )
+        ))
     };
     pin_mut!(u);
 
diff --git a/examples/nrf/src/bin/uart_idle.rs b/examples/nrf/src/bin/uart_idle.rs
index e9f4a285..e04e5cf0 100644
--- a/examples/nrf/src/bin/uart_idle.rs
+++ b/examples/nrf/src/bin/uart_idle.rs
@@ -21,9 +21,9 @@ async fn main(_spawner: Spawner, p: Peripherals) {
 
     let irq = interrupt::take!(UARTE0_UART0);
     let mut uart = unsafe {
-        uarte::UarteWithIdle::new(
+        unwrap!(uarte::UarteWithIdle::new(
             p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, irq, p.P0_08, p.P0_06, NoPin, NoPin, config,
-        )
+        ))
     };
 
     info!("uarte initialized!");

From 11655af034538b463ff8220714e9b97cf53f8f56 Mon Sep 17 00:00:00 2001
From: Dion Dokter <dion@tweedegolf.com>
Date: Mon, 18 Oct 2021 16:23:39 +0200
Subject: [PATCH 04/10] Another redo using the feedback.

PPI is now split up into PPI and DPPI under the name 'interconnect'.
The tasks and events are tracked and reset in the drop function.
---
 embassy-nrf/src/buffered_uarte.rs     |  54 ++-
 embassy-nrf/src/chips/nrf52805.rs     |  44 +--
 embassy-nrf/src/chips/nrf52810.rs     |  64 ++--
 embassy-nrf/src/chips/nrf52811.rs     |  64 ++--
 embassy-nrf/src/chips/nrf52820.rs     |  64 ++--
 embassy-nrf/src/chips/nrf52832.rs     |  64 ++--
 embassy-nrf/src/chips/nrf52833.rs     |  64 ++--
 embassy-nrf/src/chips/nrf52840.rs     |  64 ++--
 embassy-nrf/src/chips/nrf9160.rs      |  32 +-
 embassy-nrf/src/gpiote.rs             |  28 +-
 embassy-nrf/src/interconnect/dppi.rs  |  47 +++
 embassy-nrf/src/interconnect/mod.rs   | 318 ++++++++++++++++
 embassy-nrf/src/interconnect/ppi.rs   |  65 ++++
 embassy-nrf/src/lib.rs                |   2 +-
 embassy-nrf/src/ppi.rs                | 513 --------------------------
 embassy-nrf/src/timer.rs              |  38 +-
 embassy-nrf/src/uarte.rs              |  52 ++-
 examples/nrf/src/bin/buffered_uart.rs |   4 +-
 examples/nrf/src/bin/ppi.rs           |  24 +-
 examples/nrf/src/bin/uart_idle.rs     |   4 +-
 20 files changed, 729 insertions(+), 880 deletions(-)
 create mode 100644 embassy-nrf/src/interconnect/dppi.rs
 create mode 100644 embassy-nrf/src/interconnect/mod.rs
 create mode 100644 embassy-nrf/src/interconnect/ppi.rs
 delete mode 100644 embassy-nrf/src/ppi.rs

diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs
index 8ec0fb2c..7c9a2344 100644
--- a/embassy-nrf/src/buffered_uarte.rs
+++ b/embassy-nrf/src/buffered_uarte.rs
@@ -14,28 +14,15 @@ use embassy_hal_common::{low_power_wait_until, unborrow};
 
 use crate::gpio::sealed::Pin as _;
 use crate::gpio::{OptionalPin as GpioOptionalPin, Pin as GpioPin};
-use crate::ppi::{AnyChannel, Channel, Event, Ppi, Task};
+use crate::interconnect::{AnyChannel, Event, OneToOneChannel, OneToTwoChannel, Ppi, Task};
+use crate::pac;
 use crate::timer::Instance as TimerInstance;
 use crate::timer::{Frequency, Timer};
 use crate::uarte::{Config, Instance as UarteInstance};
-use crate::{pac, ppi};
 
 // Re-export SVD variants to allow user to directly set values
 pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
 
-#[non_exhaustive]
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "defmt", derive(defmt::Format))]
-pub enum Error {
-    PpiError(ppi::Error),
-}
-
-impl From<ppi::Error> for Error {
-    fn from(e: ppi::Error) -> Self {
-        Self::PpiError(e)
-    }
-}
-
 #[derive(Copy, Clone, Debug, PartialEq)]
 enum RxState {
     Idle,
@@ -58,8 +45,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> State<'d, U, T> {
 struct StateInner<'d, U: UarteInstance, T: TimerInstance> {
     phantom: PhantomData<&'d mut U>,
     timer: Timer<'d, T>,
-    _ppi_ch1: Ppi<'d, AnyChannel>,
-    _ppi_ch2: Ppi<'d, AnyChannel>,
+    _ppi_ch1: Ppi<'d, AnyChannel, 1, 2>,
+    _ppi_ch2: Ppi<'d, AnyChannel, 1, 1>,
 
     rx: RingBuffer<'d>,
     rx_state: RxState,
@@ -79,15 +66,12 @@ impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {
 
 impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
     /// unsafe: may not leak self or futures
-    ///
-    /// - *Note:* ppi_ch1 must have at least 1 free event and 2 free tasks or a PPI error is returned
-    /// - *Note:* ppi_ch2 must have at least 1 free event and 1 free tasks or a PPI error is returned
     pub unsafe fn new(
         state: &'d mut State<'d, U, T>,
         _uarte: impl Unborrow<Target = U> + 'd,
         timer: impl Unborrow<Target = T> + 'd,
-        ppi_ch1: impl Unborrow<Target = impl Channel> + 'd,
-        ppi_ch2: impl Unborrow<Target = impl Channel> + 'd,
+        ppi_ch1: impl Unborrow<Target = impl OneToTwoChannel + 'd> + 'd,
+        ppi_ch2: impl Unborrow<Target = impl OneToOneChannel + 'd> + 'd,
         irq: impl Unborrow<Target = U::Interrupt> + 'd,
         rxd: impl Unborrow<Target = impl GpioPin> + 'd,
         txd: impl Unborrow<Target = impl GpioPin> + 'd,
@@ -96,7 +80,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
         config: Config,
         rx_buffer: &'d mut [u8],
         tx_buffer: &'d mut [u8],
-    ) -> Result<Self, Error> {
+    ) -> Self {
         unborrow!(ppi_ch1, ppi_ch2, irq, rxd, txd, cts, rts);
 
         let r = U::regs();
@@ -160,18 +144,24 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
         timer.cc(0).short_compare_clear();
         timer.cc(0).short_compare_stop();
 
-        let mut ppi_ch1 = Ppi::new(ppi_ch1.degrade());
-        ppi_ch1.publish(Event::from_reg(&r.events_rxdrdy))?;
-        ppi_ch1.subscribe(timer.task_clear())?;
-        ppi_ch1.subscribe(timer.task_start())?;
+        let mut ppi_ch1 = Ppi::new_one_to_two(
+            ppi_ch1,
+            Event::from_reg(&r.events_rxdrdy),
+            timer.task_clear(),
+            timer.task_start(),
+        )
+        .degrade();
         ppi_ch1.enable();
 
-        let mut ppi_ch2 = Ppi::new(ppi_ch2.degrade());
-        ppi_ch2.publish(timer.cc(0).event_compare())?;
-        ppi_ch2.subscribe(Task::from_reg(&r.tasks_stoprx))?;
+        let mut ppi_ch2 = Ppi::new_one_to_one(
+            ppi_ch2,
+            timer.cc(0).event_compare(),
+            Task::from_reg(&r.tasks_stoprx),
+        )
+        .degrade();
         ppi_ch2.enable();
 
-        Ok(Self {
+        Self {
             inner: PeripheralMutex::new_unchecked(irq, &mut state.0, move || StateInner {
                 phantom: PhantomData,
                 timer,
@@ -186,7 +176,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
                 tx_state: TxState::Idle,
                 tx_waker: WakerRegistration::new(),
             }),
-        })
+        }
     }
 
     pub fn set_baudrate(&mut self, baudrate: Baudrate) {
diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs
index 03fc5f30..326f4a8b 100644
--- a/embassy-nrf/src/chips/nrf52805.rs
+++ b/embassy-nrf/src/chips/nrf52805.rs
@@ -167,28 +167,28 @@ impl_pin!(P0_29, 0, 29);
 impl_pin!(P0_30, 0, 30);
 impl_pin!(P0_31, 0, 31);
 
-impl_ppi_channel!(PPI_CH0, 0, 2, 1);
-impl_ppi_channel!(PPI_CH1, 1, 2, 1);
-impl_ppi_channel!(PPI_CH2, 2, 2, 1);
-impl_ppi_channel!(PPI_CH3, 3, 2, 1);
-impl_ppi_channel!(PPI_CH4, 4, 2, 1);
-impl_ppi_channel!(PPI_CH5, 5, 2, 1);
-impl_ppi_channel!(PPI_CH6, 6, 2, 1);
-impl_ppi_channel!(PPI_CH7, 7, 2, 1);
-impl_ppi_channel!(PPI_CH8, 8, 2, 1);
-impl_ppi_channel!(PPI_CH9, 9, 2, 1);
-impl_ppi_channel!(PPI_CH20, 20, 1, 0);
-impl_ppi_channel!(PPI_CH21, 21, 1, 0);
-impl_ppi_channel!(PPI_CH22, 22, 1, 0);
-impl_ppi_channel!(PPI_CH23, 23, 1, 0);
-impl_ppi_channel!(PPI_CH24, 24, 1, 0);
-impl_ppi_channel!(PPI_CH25, 25, 1, 0);
-impl_ppi_channel!(PPI_CH26, 26, 1, 0);
-impl_ppi_channel!(PPI_CH27, 27, 1, 0);
-impl_ppi_channel!(PPI_CH28, 28, 1, 0);
-impl_ppi_channel!(PPI_CH29, 29, 1, 0);
-impl_ppi_channel!(PPI_CH30, 30, 1, 0);
-impl_ppi_channel!(PPI_CH31, 31, 1, 0);
+impl_ppi_channel!(PPI_CH0, 0, true, 1, 2);
+impl_ppi_channel!(PPI_CH1, 1, true, 1, 2);
+impl_ppi_channel!(PPI_CH2, 2, true, 1, 2);
+impl_ppi_channel!(PPI_CH3, 3, true, 1, 2);
+impl_ppi_channel!(PPI_CH4, 4, true, 1, 2);
+impl_ppi_channel!(PPI_CH5, 5, true, 1, 2);
+impl_ppi_channel!(PPI_CH6, 6, true, 1, 2);
+impl_ppi_channel!(PPI_CH7, 7, true, 1, 2);
+impl_ppi_channel!(PPI_CH8, 8, true, 1, 2);
+impl_ppi_channel!(PPI_CH9, 9, true, 1, 2);
+impl_ppi_channel!(PPI_CH20, 20, false, 0, 1);
+impl_ppi_channel!(PPI_CH21, 21, false, 0, 1);
+impl_ppi_channel!(PPI_CH22, 22, false, 0, 1);
+impl_ppi_channel!(PPI_CH23, 23, false, 0, 1);
+impl_ppi_channel!(PPI_CH24, 24, false, 0, 1);
+impl_ppi_channel!(PPI_CH25, 25, false, 0, 1);
+impl_ppi_channel!(PPI_CH26, 26, false, 0, 1);
+impl_ppi_channel!(PPI_CH27, 27, false, 0, 1);
+impl_ppi_channel!(PPI_CH28, 28, false, 0, 1);
+impl_ppi_channel!(PPI_CH29, 29, false, 0, 1);
+impl_ppi_channel!(PPI_CH30, 30, false, 0, 1);
+impl_ppi_channel!(PPI_CH31, 31, false, 0, 1);
 
 impl_saadc_input!(P0_04, ANALOGINPUT2);
 impl_saadc_input!(P0_05, ANALOGINPUT3);
diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs
index e317e4c5..4c16d51a 100644
--- a/embassy-nrf/src/chips/nrf52810.rs
+++ b/embassy-nrf/src/chips/nrf52810.rs
@@ -172,38 +172,38 @@ impl_pin!(P0_29, 0, 29);
 impl_pin!(P0_30, 0, 30);
 impl_pin!(P0_31, 0, 31);
 
-impl_ppi_channel!(PPI_CH0, 0, 2, 1);
-impl_ppi_channel!(PPI_CH1, 1, 2, 1);
-impl_ppi_channel!(PPI_CH2, 2, 2, 1);
-impl_ppi_channel!(PPI_CH3, 3, 2, 1);
-impl_ppi_channel!(PPI_CH4, 4, 2, 1);
-impl_ppi_channel!(PPI_CH5, 5, 2, 1);
-impl_ppi_channel!(PPI_CH6, 6, 2, 1);
-impl_ppi_channel!(PPI_CH7, 7, 2, 1);
-impl_ppi_channel!(PPI_CH8, 8, 2, 1);
-impl_ppi_channel!(PPI_CH9, 9, 2, 1);
-impl_ppi_channel!(PPI_CH10, 10, 2, 1);
-impl_ppi_channel!(PPI_CH11, 11, 2, 1);
-impl_ppi_channel!(PPI_CH12, 12, 2, 1);
-impl_ppi_channel!(PPI_CH13, 13, 2, 1);
-impl_ppi_channel!(PPI_CH14, 14, 2, 1);
-impl_ppi_channel!(PPI_CH15, 15, 2, 1);
-impl_ppi_channel!(PPI_CH16, 16, 2, 1);
-impl_ppi_channel!(PPI_CH17, 17, 2, 1);
-impl_ppi_channel!(PPI_CH18, 18, 2, 1);
-impl_ppi_channel!(PPI_CH19, 19, 2, 1);
-impl_ppi_channel!(PPI_CH20, 20, 1, 0);
-impl_ppi_channel!(PPI_CH21, 21, 1, 0);
-impl_ppi_channel!(PPI_CH22, 22, 1, 0);
-impl_ppi_channel!(PPI_CH23, 23, 1, 0);
-impl_ppi_channel!(PPI_CH24, 24, 1, 0);
-impl_ppi_channel!(PPI_CH25, 25, 1, 0);
-impl_ppi_channel!(PPI_CH26, 26, 1, 0);
-impl_ppi_channel!(PPI_CH27, 27, 1, 0);
-impl_ppi_channel!(PPI_CH28, 28, 1, 0);
-impl_ppi_channel!(PPI_CH29, 29, 1, 0);
-impl_ppi_channel!(PPI_CH30, 30, 1, 0);
-impl_ppi_channel!(PPI_CH31, 31, 1, 0);
+impl_ppi_channel!(PPI_CH0, 0, true, 1, 2);
+impl_ppi_channel!(PPI_CH1, 1, true, 1, 2);
+impl_ppi_channel!(PPI_CH2, 2, true, 1, 2);
+impl_ppi_channel!(PPI_CH3, 3, true, 1, 2);
+impl_ppi_channel!(PPI_CH4, 4, true, 1, 2);
+impl_ppi_channel!(PPI_CH5, 5, true, 1, 2);
+impl_ppi_channel!(PPI_CH6, 6, true, 1, 2);
+impl_ppi_channel!(PPI_CH7, 7, true, 1, 2);
+impl_ppi_channel!(PPI_CH8, 8, true, 1, 2);
+impl_ppi_channel!(PPI_CH9, 9, true, 1, 2);
+impl_ppi_channel!(PPI_CH10, 10, true, 1, 2);
+impl_ppi_channel!(PPI_CH11, 11, true, 1, 2);
+impl_ppi_channel!(PPI_CH12, 12, true, 1, 2);
+impl_ppi_channel!(PPI_CH13, 13, true, 1, 2);
+impl_ppi_channel!(PPI_CH14, 14, true, 1, 2);
+impl_ppi_channel!(PPI_CH15, 15, true, 1, 2);
+impl_ppi_channel!(PPI_CH16, 16, true, 1, 2);
+impl_ppi_channel!(PPI_CH17, 17, true, 1, 2);
+impl_ppi_channel!(PPI_CH18, 18, true, 1, 2);
+impl_ppi_channel!(PPI_CH19, 19, true, 1, 2);
+impl_ppi_channel!(PPI_CH20, 20, false, 0, 1);
+impl_ppi_channel!(PPI_CH21, 21, false, 0, 1);
+impl_ppi_channel!(PPI_CH22, 22, false, 0, 1);
+impl_ppi_channel!(PPI_CH23, 23, false, 0, 1);
+impl_ppi_channel!(PPI_CH24, 24, false, 0, 1);
+impl_ppi_channel!(PPI_CH25, 25, false, 0, 1);
+impl_ppi_channel!(PPI_CH26, 26, false, 0, 1);
+impl_ppi_channel!(PPI_CH27, 27, false, 0, 1);
+impl_ppi_channel!(PPI_CH28, 28, false, 0, 1);
+impl_ppi_channel!(PPI_CH29, 29, false, 0, 1);
+impl_ppi_channel!(PPI_CH30, 30, false, 0, 1);
+impl_ppi_channel!(PPI_CH31, 31, false, 0, 1);
 
 impl_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs
index 748f5f4b..0c54d9b9 100644
--- a/embassy-nrf/src/chips/nrf52811.rs
+++ b/embassy-nrf/src/chips/nrf52811.rs
@@ -173,38 +173,38 @@ impl_pin!(P0_29, 0, 29);
 impl_pin!(P0_30, 0, 30);
 impl_pin!(P0_31, 0, 31);
 
-impl_ppi_channel!(PPI_CH0, 0, 2, 1);
-impl_ppi_channel!(PPI_CH1, 1, 2, 1);
-impl_ppi_channel!(PPI_CH2, 2, 2, 1);
-impl_ppi_channel!(PPI_CH3, 3, 2, 1);
-impl_ppi_channel!(PPI_CH4, 4, 2, 1);
-impl_ppi_channel!(PPI_CH5, 5, 2, 1);
-impl_ppi_channel!(PPI_CH6, 6, 2, 1);
-impl_ppi_channel!(PPI_CH7, 7, 2, 1);
-impl_ppi_channel!(PPI_CH8, 8, 2, 1);
-impl_ppi_channel!(PPI_CH9, 9, 2, 1);
-impl_ppi_channel!(PPI_CH10, 10, 2, 1);
-impl_ppi_channel!(PPI_CH11, 11, 2, 1);
-impl_ppi_channel!(PPI_CH12, 12, 2, 1);
-impl_ppi_channel!(PPI_CH13, 13, 2, 1);
-impl_ppi_channel!(PPI_CH14, 14, 2, 1);
-impl_ppi_channel!(PPI_CH15, 15, 2, 1);
-impl_ppi_channel!(PPI_CH16, 16, 2, 1);
-impl_ppi_channel!(PPI_CH17, 17, 2, 1);
-impl_ppi_channel!(PPI_CH18, 18, 2, 1);
-impl_ppi_channel!(PPI_CH19, 19, 2, 1);
-impl_ppi_channel!(PPI_CH20, 20, 1, 0);
-impl_ppi_channel!(PPI_CH21, 21, 1, 0);
-impl_ppi_channel!(PPI_CH22, 22, 1, 0);
-impl_ppi_channel!(PPI_CH23, 23, 1, 0);
-impl_ppi_channel!(PPI_CH24, 24, 1, 0);
-impl_ppi_channel!(PPI_CH25, 25, 1, 0);
-impl_ppi_channel!(PPI_CH26, 26, 1, 0);
-impl_ppi_channel!(PPI_CH27, 27, 1, 0);
-impl_ppi_channel!(PPI_CH28, 28, 1, 0);
-impl_ppi_channel!(PPI_CH29, 29, 1, 0);
-impl_ppi_channel!(PPI_CH30, 30, 1, 0);
-impl_ppi_channel!(PPI_CH31, 31, 1, 0);
+impl_ppi_channel!(PPI_CH0, 0, true, 1, 2);
+impl_ppi_channel!(PPI_CH1, 1, true, 1, 2);
+impl_ppi_channel!(PPI_CH2, 2, true, 1, 2);
+impl_ppi_channel!(PPI_CH3, 3, true, 1, 2);
+impl_ppi_channel!(PPI_CH4, 4, true, 1, 2);
+impl_ppi_channel!(PPI_CH5, 5, true, 1, 2);
+impl_ppi_channel!(PPI_CH6, 6, true, 1, 2);
+impl_ppi_channel!(PPI_CH7, 7, true, 1, 2);
+impl_ppi_channel!(PPI_CH8, 8, true, 1, 2);
+impl_ppi_channel!(PPI_CH9, 9, true, 1, 2);
+impl_ppi_channel!(PPI_CH10, 10, true, 1, 2);
+impl_ppi_channel!(PPI_CH11, 11, true, 1, 2);
+impl_ppi_channel!(PPI_CH12, 12, true, 1, 2);
+impl_ppi_channel!(PPI_CH13, 13, true, 1, 2);
+impl_ppi_channel!(PPI_CH14, 14, true, 1, 2);
+impl_ppi_channel!(PPI_CH15, 15, true, 1, 2);
+impl_ppi_channel!(PPI_CH16, 16, true, 1, 2);
+impl_ppi_channel!(PPI_CH17, 17, true, 1, 2);
+impl_ppi_channel!(PPI_CH18, 18, true, 1, 2);
+impl_ppi_channel!(PPI_CH19, 19, true, 1, 2);
+impl_ppi_channel!(PPI_CH20, 20, false, 0, 1);
+impl_ppi_channel!(PPI_CH21, 21, false, 0, 1);
+impl_ppi_channel!(PPI_CH22, 22, false, 0, 1);
+impl_ppi_channel!(PPI_CH23, 23, false, 0, 1);
+impl_ppi_channel!(PPI_CH24, 24, false, 0, 1);
+impl_ppi_channel!(PPI_CH25, 25, false, 0, 1);
+impl_ppi_channel!(PPI_CH26, 26, false, 0, 1);
+impl_ppi_channel!(PPI_CH27, 27, false, 0, 1);
+impl_ppi_channel!(PPI_CH28, 28, false, 0, 1);
+impl_ppi_channel!(PPI_CH29, 29, false, 0, 1);
+impl_ppi_channel!(PPI_CH30, 30, false, 0, 1);
+impl_ppi_channel!(PPI_CH31, 31, false, 0, 1);
 
 impl_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs
index 02d2c610..d52f2f98 100644
--- a/embassy-nrf/src/chips/nrf52820.rs
+++ b/embassy-nrf/src/chips/nrf52820.rs
@@ -168,38 +168,38 @@ impl_pin!(P0_29, 0, 29);
 impl_pin!(P0_30, 0, 30);
 impl_pin!(P0_31, 0, 31);
 
-impl_ppi_channel!(PPI_CH0, 0, 2, 1);
-impl_ppi_channel!(PPI_CH1, 1, 2, 1);
-impl_ppi_channel!(PPI_CH2, 2, 2, 1);
-impl_ppi_channel!(PPI_CH3, 3, 2, 1);
-impl_ppi_channel!(PPI_CH4, 4, 2, 1);
-impl_ppi_channel!(PPI_CH5, 5, 2, 1);
-impl_ppi_channel!(PPI_CH6, 6, 2, 1);
-impl_ppi_channel!(PPI_CH7, 7, 2, 1);
-impl_ppi_channel!(PPI_CH8, 8, 2, 1);
-impl_ppi_channel!(PPI_CH9, 9, 2, 1);
-impl_ppi_channel!(PPI_CH10, 10, 2, 1);
-impl_ppi_channel!(PPI_CH11, 11, 2, 1);
-impl_ppi_channel!(PPI_CH12, 12, 2, 1);
-impl_ppi_channel!(PPI_CH13, 13, 2, 1);
-impl_ppi_channel!(PPI_CH14, 14, 2, 1);
-impl_ppi_channel!(PPI_CH15, 15, 2, 1);
-impl_ppi_channel!(PPI_CH16, 16, 2, 1);
-impl_ppi_channel!(PPI_CH17, 17, 2, 1);
-impl_ppi_channel!(PPI_CH18, 18, 2, 1);
-impl_ppi_channel!(PPI_CH19, 19, 2, 1);
-impl_ppi_channel!(PPI_CH20, 20, 1, 0);
-impl_ppi_channel!(PPI_CH21, 21, 1, 0);
-impl_ppi_channel!(PPI_CH22, 22, 1, 0);
-impl_ppi_channel!(PPI_CH23, 23, 1, 0);
-impl_ppi_channel!(PPI_CH24, 24, 1, 0);
-impl_ppi_channel!(PPI_CH25, 25, 1, 0);
-impl_ppi_channel!(PPI_CH26, 26, 1, 0);
-impl_ppi_channel!(PPI_CH27, 27, 1, 0);
-impl_ppi_channel!(PPI_CH28, 28, 1, 0);
-impl_ppi_channel!(PPI_CH29, 29, 1, 0);
-impl_ppi_channel!(PPI_CH30, 30, 1, 0);
-impl_ppi_channel!(PPI_CH31, 31, 1, 0);
+impl_ppi_channel!(PPI_CH0, 0, true, 1, 2);
+impl_ppi_channel!(PPI_CH1, 1, true, 1, 2);
+impl_ppi_channel!(PPI_CH2, 2, true, 1, 2);
+impl_ppi_channel!(PPI_CH3, 3, true, 1, 2);
+impl_ppi_channel!(PPI_CH4, 4, true, 1, 2);
+impl_ppi_channel!(PPI_CH5, 5, true, 1, 2);
+impl_ppi_channel!(PPI_CH6, 6, true, 1, 2);
+impl_ppi_channel!(PPI_CH7, 7, true, 1, 2);
+impl_ppi_channel!(PPI_CH8, 8, true, 1, 2);
+impl_ppi_channel!(PPI_CH9, 9, true, 1, 2);
+impl_ppi_channel!(PPI_CH10, 10, true, 1, 2);
+impl_ppi_channel!(PPI_CH11, 11, true, 1, 2);
+impl_ppi_channel!(PPI_CH12, 12, true, 1, 2);
+impl_ppi_channel!(PPI_CH13, 13, true, 1, 2);
+impl_ppi_channel!(PPI_CH14, 14, true, 1, 2);
+impl_ppi_channel!(PPI_CH15, 15, true, 1, 2);
+impl_ppi_channel!(PPI_CH16, 16, true, 1, 2);
+impl_ppi_channel!(PPI_CH17, 17, true, 1, 2);
+impl_ppi_channel!(PPI_CH18, 18, true, 1, 2);
+impl_ppi_channel!(PPI_CH19, 19, true, 1, 2);
+impl_ppi_channel!(PPI_CH20, 20, false, 0, 1);
+impl_ppi_channel!(PPI_CH21, 21, false, 0, 1);
+impl_ppi_channel!(PPI_CH22, 22, false, 0, 1);
+impl_ppi_channel!(PPI_CH23, 23, false, 0, 1);
+impl_ppi_channel!(PPI_CH24, 24, false, 0, 1);
+impl_ppi_channel!(PPI_CH25, 25, false, 0, 1);
+impl_ppi_channel!(PPI_CH26, 26, false, 0, 1);
+impl_ppi_channel!(PPI_CH27, 27, false, 0, 1);
+impl_ppi_channel!(PPI_CH28, 28, false, 0, 1);
+impl_ppi_channel!(PPI_CH29, 29, false, 0, 1);
+impl_ppi_channel!(PPI_CH30, 30, false, 0, 1);
+impl_ppi_channel!(PPI_CH31, 31, false, 0, 1);
 
 pub mod irqs {
     use crate::pac::Interrupt as InterruptEnum;
diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs
index 611dbbd5..d97a6535 100644
--- a/embassy-nrf/src/chips/nrf52832.rs
+++ b/embassy-nrf/src/chips/nrf52832.rs
@@ -190,38 +190,38 @@ impl_pin!(P0_29, 0, 29);
 impl_pin!(P0_30, 0, 30);
 impl_pin!(P0_31, 0, 31);
 
-impl_ppi_channel!(PPI_CH0, 0, 2, 1);
-impl_ppi_channel!(PPI_CH1, 1, 2, 1);
-impl_ppi_channel!(PPI_CH2, 2, 2, 1);
-impl_ppi_channel!(PPI_CH3, 3, 2, 1);
-impl_ppi_channel!(PPI_CH4, 4, 2, 1);
-impl_ppi_channel!(PPI_CH5, 5, 2, 1);
-impl_ppi_channel!(PPI_CH6, 6, 2, 1);
-impl_ppi_channel!(PPI_CH7, 7, 2, 1);
-impl_ppi_channel!(PPI_CH8, 8, 2, 1);
-impl_ppi_channel!(PPI_CH9, 9, 2, 1);
-impl_ppi_channel!(PPI_CH10, 10, 2, 1);
-impl_ppi_channel!(PPI_CH11, 11, 2, 1);
-impl_ppi_channel!(PPI_CH12, 12, 2, 1);
-impl_ppi_channel!(PPI_CH13, 13, 2, 1);
-impl_ppi_channel!(PPI_CH14, 14, 2, 1);
-impl_ppi_channel!(PPI_CH15, 15, 2, 1);
-impl_ppi_channel!(PPI_CH16, 16, 2, 1);
-impl_ppi_channel!(PPI_CH17, 17, 2, 1);
-impl_ppi_channel!(PPI_CH18, 18, 2, 1);
-impl_ppi_channel!(PPI_CH19, 19, 2, 1);
-impl_ppi_channel!(PPI_CH20, 20, 1, 0);
-impl_ppi_channel!(PPI_CH21, 21, 1, 0);
-impl_ppi_channel!(PPI_CH22, 22, 1, 0);
-impl_ppi_channel!(PPI_CH23, 23, 1, 0);
-impl_ppi_channel!(PPI_CH24, 24, 1, 0);
-impl_ppi_channel!(PPI_CH25, 25, 1, 0);
-impl_ppi_channel!(PPI_CH26, 26, 1, 0);
-impl_ppi_channel!(PPI_CH27, 27, 1, 0);
-impl_ppi_channel!(PPI_CH28, 28, 1, 0);
-impl_ppi_channel!(PPI_CH29, 29, 1, 0);
-impl_ppi_channel!(PPI_CH30, 30, 1, 0);
-impl_ppi_channel!(PPI_CH31, 31, 1, 0);
+impl_ppi_channel!(PPI_CH0, 0, true, 1, 2);
+impl_ppi_channel!(PPI_CH1, 1, true, 1, 2);
+impl_ppi_channel!(PPI_CH2, 2, true, 1, 2);
+impl_ppi_channel!(PPI_CH3, 3, true, 1, 2);
+impl_ppi_channel!(PPI_CH4, 4, true, 1, 2);
+impl_ppi_channel!(PPI_CH5, 5, true, 1, 2);
+impl_ppi_channel!(PPI_CH6, 6, true, 1, 2);
+impl_ppi_channel!(PPI_CH7, 7, true, 1, 2);
+impl_ppi_channel!(PPI_CH8, 8, true, 1, 2);
+impl_ppi_channel!(PPI_CH9, 9, true, 1, 2);
+impl_ppi_channel!(PPI_CH10, 10, true, 1, 2);
+impl_ppi_channel!(PPI_CH11, 11, true, 1, 2);
+impl_ppi_channel!(PPI_CH12, 12, true, 1, 2);
+impl_ppi_channel!(PPI_CH13, 13, true, 1, 2);
+impl_ppi_channel!(PPI_CH14, 14, true, 1, 2);
+impl_ppi_channel!(PPI_CH15, 15, true, 1, 2);
+impl_ppi_channel!(PPI_CH16, 16, true, 1, 2);
+impl_ppi_channel!(PPI_CH17, 17, true, 1, 2);
+impl_ppi_channel!(PPI_CH18, 18, true, 1, 2);
+impl_ppi_channel!(PPI_CH19, 19, true, 1, 2);
+impl_ppi_channel!(PPI_CH20, 20, false, 0, 1);
+impl_ppi_channel!(PPI_CH21, 21, false, 0, 1);
+impl_ppi_channel!(PPI_CH22, 22, false, 0, 1);
+impl_ppi_channel!(PPI_CH23, 23, false, 0, 1);
+impl_ppi_channel!(PPI_CH24, 24, false, 0, 1);
+impl_ppi_channel!(PPI_CH25, 25, false, 0, 1);
+impl_ppi_channel!(PPI_CH26, 26, false, 0, 1);
+impl_ppi_channel!(PPI_CH27, 27, false, 0, 1);
+impl_ppi_channel!(PPI_CH28, 28, false, 0, 1);
+impl_ppi_channel!(PPI_CH29, 29, false, 0, 1);
+impl_ppi_channel!(PPI_CH30, 30, false, 0, 1);
+impl_ppi_channel!(PPI_CH31, 31, false, 0, 1);
 
 impl_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs
index 6f70f2a4..9bf370c4 100644
--- a/embassy-nrf/src/chips/nrf52833.rs
+++ b/embassy-nrf/src/chips/nrf52833.rs
@@ -226,38 +226,38 @@ impl_pin!(P1_13, 1, 13);
 impl_pin!(P1_14, 1, 14);
 impl_pin!(P1_15, 1, 15);
 
-impl_ppi_channel!(PPI_CH0, 0, 2, 1);
-impl_ppi_channel!(PPI_CH1, 1, 2, 1);
-impl_ppi_channel!(PPI_CH2, 2, 2, 1);
-impl_ppi_channel!(PPI_CH3, 3, 2, 1);
-impl_ppi_channel!(PPI_CH4, 4, 2, 1);
-impl_ppi_channel!(PPI_CH5, 5, 2, 1);
-impl_ppi_channel!(PPI_CH6, 6, 2, 1);
-impl_ppi_channel!(PPI_CH7, 7, 2, 1);
-impl_ppi_channel!(PPI_CH8, 8, 2, 1);
-impl_ppi_channel!(PPI_CH9, 9, 2, 1);
-impl_ppi_channel!(PPI_CH10, 10, 2, 1);
-impl_ppi_channel!(PPI_CH11, 11, 2, 1);
-impl_ppi_channel!(PPI_CH12, 12, 2, 1);
-impl_ppi_channel!(PPI_CH13, 13, 2, 1);
-impl_ppi_channel!(PPI_CH14, 14, 2, 1);
-impl_ppi_channel!(PPI_CH15, 15, 2, 1);
-impl_ppi_channel!(PPI_CH16, 16, 2, 1);
-impl_ppi_channel!(PPI_CH17, 17, 2, 1);
-impl_ppi_channel!(PPI_CH18, 18, 2, 1);
-impl_ppi_channel!(PPI_CH19, 19, 2, 1);
-impl_ppi_channel!(PPI_CH20, 20, 1, 0);
-impl_ppi_channel!(PPI_CH21, 21, 1, 0);
-impl_ppi_channel!(PPI_CH22, 22, 1, 0);
-impl_ppi_channel!(PPI_CH23, 23, 1, 0);
-impl_ppi_channel!(PPI_CH24, 24, 1, 0);
-impl_ppi_channel!(PPI_CH25, 25, 1, 0);
-impl_ppi_channel!(PPI_CH26, 26, 1, 0);
-impl_ppi_channel!(PPI_CH27, 27, 1, 0);
-impl_ppi_channel!(PPI_CH28, 28, 1, 0);
-impl_ppi_channel!(PPI_CH29, 29, 1, 0);
-impl_ppi_channel!(PPI_CH30, 30, 1, 0);
-impl_ppi_channel!(PPI_CH31, 31, 1, 0);
+impl_ppi_channel!(PPI_CH0, 0, true, 1, 2);
+impl_ppi_channel!(PPI_CH1, 1, true, 1, 2);
+impl_ppi_channel!(PPI_CH2, 2, true, 1, 2);
+impl_ppi_channel!(PPI_CH3, 3, true, 1, 2);
+impl_ppi_channel!(PPI_CH4, 4, true, 1, 2);
+impl_ppi_channel!(PPI_CH5, 5, true, 1, 2);
+impl_ppi_channel!(PPI_CH6, 6, true, 1, 2);
+impl_ppi_channel!(PPI_CH7, 7, true, 1, 2);
+impl_ppi_channel!(PPI_CH8, 8, true, 1, 2);
+impl_ppi_channel!(PPI_CH9, 9, true, 1, 2);
+impl_ppi_channel!(PPI_CH10, 10, true, 1, 2);
+impl_ppi_channel!(PPI_CH11, 11, true, 1, 2);
+impl_ppi_channel!(PPI_CH12, 12, true, 1, 2);
+impl_ppi_channel!(PPI_CH13, 13, true, 1, 2);
+impl_ppi_channel!(PPI_CH14, 14, true, 1, 2);
+impl_ppi_channel!(PPI_CH15, 15, true, 1, 2);
+impl_ppi_channel!(PPI_CH16, 16, true, 1, 2);
+impl_ppi_channel!(PPI_CH17, 17, true, 1, 2);
+impl_ppi_channel!(PPI_CH18, 18, true, 1, 2);
+impl_ppi_channel!(PPI_CH19, 19, true, 1, 2);
+impl_ppi_channel!(PPI_CH20, 20, false, 0, 1);
+impl_ppi_channel!(PPI_CH21, 21, false, 0, 1);
+impl_ppi_channel!(PPI_CH22, 22, false, 0, 1);
+impl_ppi_channel!(PPI_CH23, 23, false, 0, 1);
+impl_ppi_channel!(PPI_CH24, 24, false, 0, 1);
+impl_ppi_channel!(PPI_CH25, 25, false, 0, 1);
+impl_ppi_channel!(PPI_CH26, 26, false, 0, 1);
+impl_ppi_channel!(PPI_CH27, 27, false, 0, 1);
+impl_ppi_channel!(PPI_CH28, 28, false, 0, 1);
+impl_ppi_channel!(PPI_CH29, 29, false, 0, 1);
+impl_ppi_channel!(PPI_CH30, 30, false, 0, 1);
+impl_ppi_channel!(PPI_CH31, 31, false, 0, 1);
 
 impl_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs
index 1b982415..266328d5 100644
--- a/embassy-nrf/src/chips/nrf52840.rs
+++ b/embassy-nrf/src/chips/nrf52840.rs
@@ -231,38 +231,38 @@ impl_pin!(P1_13, 1, 13);
 impl_pin!(P1_14, 1, 14);
 impl_pin!(P1_15, 1, 15);
 
-impl_ppi_channel!(PPI_CH0, 0, 2, 1);
-impl_ppi_channel!(PPI_CH1, 1, 2, 1);
-impl_ppi_channel!(PPI_CH2, 2, 2, 1);
-impl_ppi_channel!(PPI_CH3, 3, 2, 1);
-impl_ppi_channel!(PPI_CH4, 4, 2, 1);
-impl_ppi_channel!(PPI_CH5, 5, 2, 1);
-impl_ppi_channel!(PPI_CH6, 6, 2, 1);
-impl_ppi_channel!(PPI_CH7, 7, 2, 1);
-impl_ppi_channel!(PPI_CH8, 8, 2, 1);
-impl_ppi_channel!(PPI_CH9, 9, 2, 1);
-impl_ppi_channel!(PPI_CH10, 10, 2, 1);
-impl_ppi_channel!(PPI_CH11, 11, 2, 1);
-impl_ppi_channel!(PPI_CH12, 12, 2, 1);
-impl_ppi_channel!(PPI_CH13, 13, 2, 1);
-impl_ppi_channel!(PPI_CH14, 14, 2, 1);
-impl_ppi_channel!(PPI_CH15, 15, 2, 1);
-impl_ppi_channel!(PPI_CH16, 16, 2, 1);
-impl_ppi_channel!(PPI_CH17, 17, 2, 1);
-impl_ppi_channel!(PPI_CH18, 18, 2, 1);
-impl_ppi_channel!(PPI_CH19, 19, 2, 1);
-impl_ppi_channel!(PPI_CH20, 20, 1, 0);
-impl_ppi_channel!(PPI_CH21, 21, 1, 0);
-impl_ppi_channel!(PPI_CH22, 22, 1, 0);
-impl_ppi_channel!(PPI_CH23, 23, 1, 0);
-impl_ppi_channel!(PPI_CH24, 24, 1, 0);
-impl_ppi_channel!(PPI_CH25, 25, 1, 0);
-impl_ppi_channel!(PPI_CH26, 26, 1, 0);
-impl_ppi_channel!(PPI_CH27, 27, 1, 0);
-impl_ppi_channel!(PPI_CH28, 28, 1, 0);
-impl_ppi_channel!(PPI_CH29, 29, 1, 0);
-impl_ppi_channel!(PPI_CH30, 30, 1, 0);
-impl_ppi_channel!(PPI_CH31, 31, 1, 0);
+impl_ppi_channel!(PPI_CH0, 0, true, 1, 2);
+impl_ppi_channel!(PPI_CH1, 1, true, 1, 2);
+impl_ppi_channel!(PPI_CH2, 2, true, 1, 2);
+impl_ppi_channel!(PPI_CH3, 3, true, 1, 2);
+impl_ppi_channel!(PPI_CH4, 4, true, 1, 2);
+impl_ppi_channel!(PPI_CH5, 5, true, 1, 2);
+impl_ppi_channel!(PPI_CH6, 6, true, 1, 2);
+impl_ppi_channel!(PPI_CH7, 7, true, 1, 2);
+impl_ppi_channel!(PPI_CH8, 8, true, 1, 2);
+impl_ppi_channel!(PPI_CH9, 9, true, 1, 2);
+impl_ppi_channel!(PPI_CH10, 10, true, 1, 2);
+impl_ppi_channel!(PPI_CH11, 11, true, 1, 2);
+impl_ppi_channel!(PPI_CH12, 12, true, 1, 2);
+impl_ppi_channel!(PPI_CH13, 13, true, 1, 2);
+impl_ppi_channel!(PPI_CH14, 14, true, 1, 2);
+impl_ppi_channel!(PPI_CH15, 15, true, 1, 2);
+impl_ppi_channel!(PPI_CH16, 16, true, 1, 2);
+impl_ppi_channel!(PPI_CH17, 17, true, 1, 2);
+impl_ppi_channel!(PPI_CH18, 18, true, 1, 2);
+impl_ppi_channel!(PPI_CH19, 19, true, 1, 2);
+impl_ppi_channel!(PPI_CH20, 20, false, 0, 1);
+impl_ppi_channel!(PPI_CH21, 21, false, 0, 1);
+impl_ppi_channel!(PPI_CH22, 22, false, 0, 1);
+impl_ppi_channel!(PPI_CH23, 23, false, 0, 1);
+impl_ppi_channel!(PPI_CH24, 24, false, 0, 1);
+impl_ppi_channel!(PPI_CH25, 25, false, 0, 1);
+impl_ppi_channel!(PPI_CH26, 26, false, 0, 1);
+impl_ppi_channel!(PPI_CH27, 27, false, 0, 1);
+impl_ppi_channel!(PPI_CH28, 28, false, 0, 1);
+impl_ppi_channel!(PPI_CH29, 29, false, 0, 1);
+impl_ppi_channel!(PPI_CH30, 30, false, 0, 1);
+impl_ppi_channel!(PPI_CH31, 31, false, 0, 1);
 
 impl_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs
index 42053d08..7357e9e2 100644
--- a/embassy-nrf/src/chips/nrf9160.rs
+++ b/embassy-nrf/src/chips/nrf9160.rs
@@ -184,22 +184,22 @@ impl_pin!(P0_29, 0, 29);
 impl_pin!(P0_30, 0, 30);
 impl_pin!(P0_31, 0, 31);
 
-impl_ppi_channel!(PPI_CH0, 0);
-impl_ppi_channel!(PPI_CH1, 1);
-impl_ppi_channel!(PPI_CH2, 2);
-impl_ppi_channel!(PPI_CH3, 3);
-impl_ppi_channel!(PPI_CH4, 4);
-impl_ppi_channel!(PPI_CH5, 5);
-impl_ppi_channel!(PPI_CH6, 6);
-impl_ppi_channel!(PPI_CH7, 7);
-impl_ppi_channel!(PPI_CH8, 8);
-impl_ppi_channel!(PPI_CH9, 9);
-impl_ppi_channel!(PPI_CH10, 10);
-impl_ppi_channel!(PPI_CH11, 11);
-impl_ppi_channel!(PPI_CH12, 12);
-impl_ppi_channel!(PPI_CH13, 13);
-impl_ppi_channel!(PPI_CH14, 14);
-impl_ppi_channel!(PPI_CH15, 15);
+impl_ppi_channel!(PPI_CH0, 0, true, many, many);
+impl_ppi_channel!(PPI_CH1, 1, true, many, many);
+impl_ppi_channel!(PPI_CH2, 2, true, many, many);
+impl_ppi_channel!(PPI_CH3, 3, true, many, many);
+impl_ppi_channel!(PPI_CH4, 4, true, many, many);
+impl_ppi_channel!(PPI_CH5, 5, true, many, many);
+impl_ppi_channel!(PPI_CH6, 6, true, many, many);
+impl_ppi_channel!(PPI_CH7, 7, true, many, many);
+impl_ppi_channel!(PPI_CH8, 8, true, many, many);
+impl_ppi_channel!(PPI_CH9, 9, true, many, many);
+impl_ppi_channel!(PPI_CH10, 10, true, many, many);
+impl_ppi_channel!(PPI_CH11, 11, true, many, many);
+impl_ppi_channel!(PPI_CH12, 12, true, many, many);
+impl_ppi_channel!(PPI_CH13, 13, true, many, many);
+impl_ppi_channel!(PPI_CH14, 14, true, many, many);
+impl_ppi_channel!(PPI_CH15, 15, true, many, many);
 
 impl_saadc_input!(P0_13, ANALOGINPUT0);
 impl_saadc_input!(P0_14, ANALOGINPUT1);
diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs
index 943ff530..6703bfc6 100644
--- a/embassy-nrf/src/gpiote.rs
+++ b/embassy-nrf/src/gpiote.rs
@@ -11,8 +11,8 @@ use futures::future::poll_fn;
 
 use crate::gpio::sealed::Pin as _;
 use crate::gpio::{AnyPin, Input, Output, Pin as GpioPin};
+use crate::interconnect::{Event, Task};
 use crate::pac;
-use crate::ppi::{Event, Task};
 use crate::{interrupt, peripherals};
 
 pub const CHANNEL_COUNT: usize = 8;
@@ -190,13 +190,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
     /// Returns the IN event, for use with PPI.
     pub fn event_in(&self) -> Event {
         let g = unsafe { &*pac::GPIOTE::ptr() };
-
-        #[cfg(feature = "_ppi")]
-        let reg = &g.events_in[self.ch.number()];
-        #[cfg(feature = "_dppi")]
-        let reg = &g.publish_in[self.ch.number()];
-
-        Event::from_reg(reg)
+        Event::from_reg(&g.events_in[self.ch.number()])
     }
 }
 
@@ -277,33 +271,21 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
     /// Returns the OUT task, for use with PPI.
     pub fn task_out(&self) -> Task {
         let g = unsafe { &*pac::GPIOTE::ptr() };
-        #[cfg(feature = "_ppi")]
-        let reg = &g.tasks_out[self.ch.number()];
-        #[cfg(feature = "_dppi")]
-        let reg = &g.subscribe_out[self.ch.number()];
-        Task::from_reg(reg)
+        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 {
         let g = unsafe { &*pac::GPIOTE::ptr() };
-        #[cfg(feature = "_ppi")]
-        let reg = &g.tasks_clr[self.ch.number()];
-        #[cfg(feature = "_dppi")]
-        let reg = &g.subscribe_clr[self.ch.number()];
-        Task::from_reg(reg)
+        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 {
         let g = unsafe { &*pac::GPIOTE::ptr() };
-        #[cfg(feature = "_ppi")]
-        let reg = &g.tasks_set[self.ch.number()];
-        #[cfg(feature = "_dppi")]
-        let reg = &g.subscribe_set[self.ch.number()];
-        Task::from_reg(reg)
+        Task::from_reg(&g.tasks_set[self.ch.number()])
     }
 }
 
diff --git a/embassy-nrf/src/interconnect/dppi.rs b/embassy-nrf/src/interconnect/dppi.rs
new file mode 100644
index 00000000..60f19fca
--- /dev/null
+++ b/embassy-nrf/src/interconnect/dppi.rs
@@ -0,0 +1,47 @@
+use super::{Channel, Event, Ppi, Task};
+
+const DPPI_ENABLE_BIT: u32 = 0x8000_0000;
+const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF;
+const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
+
+impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
+    Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
+{
+    pub(super) fn enable_task(task: &Task, channel: &C, _index: usize) {
+        unsafe {
+            task.0
+                .as_ptr()
+                .add(REGISTER_DPPI_CONFIG_OFFSET)
+                .write_volatile(DPPI_ENABLE_BIT | (channel.number() as u32 & DPPI_CHANNEL_MASK));
+        }
+    }
+
+    pub(super) fn disable_task(task: &Task, _channel: &C, _index: usize) {
+        unsafe {
+            task.0
+                .as_ptr()
+                .add(REGISTER_DPPI_CONFIG_OFFSET)
+                .write_volatile(0);
+        }
+    }
+
+    pub(super) fn enable_event(event: &Event, channel: &C, _index: usize) {
+        unsafe {
+            event
+                .0
+                .as_ptr()
+                .add(REGISTER_DPPI_CONFIG_OFFSET)
+                .write_volatile(DPPI_ENABLE_BIT | (channel.number() as u32 & DPPI_CHANNEL_MASK));
+        }
+    }
+
+    pub(super) fn disable_event(event: &Event, _channel: &C, _index: usize) {
+        unsafe {
+            event
+                .0
+                .as_ptr()
+                .add(REGISTER_DPPI_CONFIG_OFFSET)
+                .write_volatile(0);
+        }
+    }
+}
diff --git a/embassy-nrf/src/interconnect/mod.rs b/embassy-nrf/src/interconnect/mod.rs
new file mode 100644
index 00000000..23cc4527
--- /dev/null
+++ b/embassy-nrf/src/interconnect/mod.rs
@@ -0,0 +1,318 @@
+#![macro_use]
+
+//! HAL interface for the PPI and DPPI peripheral.
+//!
+//! The (Distributed) Programmable Peripheral Interconnect interface allows for an autonomous interoperability
+//! between peripherals through their events and tasks. There are fixed PPI channels and fully
+//! configurable ones. Fixed channels can only connect specific events to specific tasks. For fully
+//! configurable channels, it is possible to choose, via software, the event and the task that it
+//! will triggered by the event.
+//!
+//! On nRF52 devices, there is also a fork task endpoint, where the user can configure one more task
+//! to be triggered by the same event, even fixed PPI channels have a configurable fork task.
+//! 
+//! The DPPI for nRF53 and nRF91 devices works in a different way. Every channel can support infinitely
+//! many tasks and events, but any single task or event can only be coupled with one channel.
+//! 
+
+use crate::{pac, peripherals};
+use core::marker::PhantomData;
+use core::ptr::NonNull;
+use embassy::util::Unborrow;
+use embassy_hal_common::{unborrow, unsafe_impl_unborrow};
+
+#[cfg(feature = "_dppi")]
+mod dppi;
+#[cfg(feature = "_ppi")]
+mod ppi;
+
+pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
+    ch: C,
+    events: [Event; EVENT_COUNT],
+    tasks: [Task; TASK_COUNT],
+    phantom: PhantomData<&'d mut C>,
+}
+
+impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
+    Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
+{
+    pub fn degrade(self) -> Ppi<'d, AnyChannel, EVENT_COUNT, TASK_COUNT> {
+        Ppi {
+            ch: AnyChannel {
+                number: self.ch.number() as u8,
+                #[cfg(feature = "_ppi")]
+                has_configurable_task: self.ch.is_task_configurable(),
+            },
+            events: self.events,
+            tasks: self.tasks,
+            phantom: PhantomData,
+        }
+    }
+
+    /// Enables the channel.
+    pub fn enable(&mut self) {
+        let r = unsafe { &*pac::PPI::ptr() };
+        r.chenset
+            .write(|w| unsafe { w.bits(1 << self.ch.number()) });
+    }
+
+    /// Disables the channel.
+    pub fn disable(&mut self) {
+        let r = unsafe { &*pac::PPI::ptr() };
+        r.chenclr
+            .write(|w| unsafe { w.bits(1 << self.ch.number()) });
+    }
+
+    /// Enables all tasks and events
+    fn enable_all(&self) {
+        for (index, task) in self.tasks.iter().enumerate() {
+            Self::enable_task(task, &self.ch, index);
+        }
+        for (index, event) in self.events.iter().enumerate() {
+            Self::enable_event(event, &self.ch, index);
+        }
+    }
+
+    /// Disable all tasks and events
+    fn disable_all(&self) {
+        for (index, task) in self.tasks.iter().enumerate() {
+            Self::disable_task(task, &self.ch, index);
+        }
+        for (index, event) in self.events.iter().enumerate() {
+            Self::disable_event(event, &self.ch, index);
+        }
+    }
+}
+
+impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop
+    for Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
+{
+    fn drop(&mut self) {
+        self.disable();
+        self.disable_all();
+    }
+}
+
+impl<'d, C: StaticToOneChannel> Ppi<'d, C, 0, 1> {
+    pub fn new_static_to_one(ch: impl Unborrow<Target = C> + 'd, task: Task) -> Self {
+        unborrow!(ch);
+
+        let s = Self {
+            ch,
+            events: [],
+            tasks: [task],
+            phantom: PhantomData,
+        };
+
+        s.enable_all();
+        s
+    }
+}
+
+impl<'d, C: OneToOneChannel> Ppi<'d, C, 1, 1> {
+    pub fn new_one_to_one(ch: impl Unborrow<Target = C> + 'd, event: Event, task: Task) -> Self {
+        unborrow!(ch);
+
+        let s = Self {
+            ch,
+            events: [event],
+            tasks: [task],
+            phantom: PhantomData,
+        };
+
+        s.enable_all();
+        s
+    }
+}
+
+impl<'d, C: OneToTwoChannel> Ppi<'d, C, 1, 2> {
+    pub fn new_one_to_two(
+        ch: impl Unborrow<Target = C> + 'd,
+        event: Event,
+        task1: Task,
+        task2: Task,
+    ) -> Self {
+        unborrow!(ch);
+
+        let s = Self {
+            ch,
+            events: [event],
+            tasks: [task1, task2],
+            phantom: PhantomData,
+        };
+
+        s.enable_all();
+        s
+    }
+}
+
+impl<'d, C: ManyToManyChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
+    Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
+{
+    pub fn new_many_to_many(
+        ch: impl Unborrow<Target = C> + 'd,
+        events: [Event; EVENT_COUNT],
+        tasks: [Task; TASK_COUNT],
+    ) -> Self {
+        unborrow!(ch);
+
+        let s = Self {
+            ch,
+            events,
+            tasks,
+            phantom: PhantomData,
+        };
+
+        s.enable_all();
+        s
+    }
+}
+
+/// Represents a task that a peripheral can do.
+/// When a task is subscribed to a PPI channel it will run when the channel is triggered by
+/// a published event.
+///
+/// The pointer is to a task register
+#[derive(PartialEq, Eq, Clone, Copy)]
+pub struct Task(pub NonNull<u32>);
+impl Task {
+    pub(crate) fn from_reg<T>(reg: &T) -> Self {
+        Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
+    }
+}
+
+/// # Safety
+///
+/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
+unsafe impl Send for Task {}
+
+/// Represents an event that a peripheral can publish.
+/// An event can be set to publish on a PPI channel when the event happens.
+///
+/// The pointer is to an event register
+#[derive(PartialEq, Eq, Clone, Copy)]
+pub struct Event(pub NonNull<u32>);
+impl Event {
+    pub(crate) fn from_reg<T>(reg: &T) -> Self {
+        Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
+    }
+}
+
+/// # Safety
+///
+/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
+unsafe impl Send for Event {}
+
+// ======================
+//       traits
+
+pub(crate) mod sealed {
+    pub trait Channel {}
+    pub trait Group {}
+}
+
+pub trait Channel: sealed::Channel + Unborrow<Target = Self> + Sized {
+    /// Returns the number of the channel
+    fn number(&self) -> usize;
+    #[cfg(feature = "_ppi")]
+    fn is_task_configurable(&self) -> bool;
+}
+
+pub trait StaticToOneChannel: Channel {}
+pub trait OneToOneChannel: StaticToOneChannel {}
+pub trait OneToTwoChannel: OneToOneChannel {}
+pub trait ManyToManyChannel: OneToTwoChannel {}
+
+pub trait Group: sealed::Group + Sized {
+    fn number(&self) -> usize;
+}
+
+// ======================
+//       channels
+
+pub struct AnyChannel {
+    number: u8,
+    #[cfg(feature = "_ppi")]
+    has_configurable_task: bool,
+}
+unsafe_impl_unborrow!(AnyChannel);
+impl sealed::Channel for AnyChannel {}
+impl Channel for AnyChannel {
+    fn number(&self) -> usize {
+        self.number as usize
+    }
+
+    #[cfg(feature = "_ppi")]
+    fn is_task_configurable(&self) -> bool {
+        self.has_configurable_task
+    }
+}
+
+macro_rules! impl_ppi_channel {
+    ($type:ident, $number:expr, $has_configurable_task:expr) => {
+        impl crate::interconnect::sealed::Channel for peripherals::$type {}
+        impl crate::interconnect::Channel for peripherals::$type {
+            fn number(&self) -> usize {
+                $number
+            }
+
+            #[cfg(feature = "_ppi")]
+            fn is_task_configurable(&self) -> bool {
+                $has_configurable_task
+            }
+        }
+    };
+    ($type:ident, $number:expr, $has_configurable_task:expr, 0, 0) => {
+        impl_ppi_channel!($type, $number, $has_configurable_task);
+    };
+    ($type:ident, $number:expr, $has_configurable_task:expr, 0, 1) => {
+        impl_ppi_channel!($type, $number, $has_configurable_task, 0, 0);
+        impl crate::interconnect::StaticToOneChannel for peripherals::$type {}
+    };
+    ($type:ident, $number:expr, $has_configurable_task:expr, 1, 1) => {
+        impl_ppi_channel!($type, $number, $has_configurable_task, 0, 1);
+        impl crate::interconnect::OneToOneChannel for peripherals::$type {}
+    };
+    ($type:ident, $number:expr, $has_configurable_task:expr, 1, 2) => {
+        impl_ppi_channel!($type, $number, $has_configurable_task, 1, 1);
+        impl crate::interconnect::OneToTwoChannel for peripherals::$type {}
+    };
+    ($type:ident, $number:expr, $has_configurable_task:expr, many, many) => {
+        impl_ppi_channel!($type, $number, $has_configurable_task, 1, 2);
+        impl crate::interconnect::ManyToManyChannel for peripherals::$type {}
+    };
+}
+
+// ======================
+//       groups
+
+pub struct AnyGroup {
+    number: u8,
+}
+unsafe_impl_unborrow!(AnyGroup);
+impl sealed::Group for AnyGroup {}
+impl Group for AnyGroup {
+    fn number(&self) -> usize {
+        self.number as usize
+    }
+}
+
+macro_rules! impl_group {
+    ($type:ident, $number:expr) => {
+        impl sealed::Group for peripherals::$type {}
+        impl Group for peripherals::$type {
+            fn number(&self) -> usize {
+                $number
+            }
+        }
+    };
+}
+
+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/interconnect/ppi.rs b/embassy-nrf/src/interconnect/ppi.rs
new file mode 100644
index 00000000..91ff811f
--- /dev/null
+++ b/embassy-nrf/src/interconnect/ppi.rs
@@ -0,0 +1,65 @@
+use super::{Channel, Event, Ppi, Task};
+use crate::pac;
+
+impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
+    Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
+{
+    pub(super) fn enable_task(task: &Task, channel: &C, index: usize) {
+        match (index, channel.is_task_configurable()) {
+            (0, false) => Self::set_fork_task(Some(task), channel.number()), // Static channel with fork
+            (0, true) => Self::set_main_task(Some(task), channel.number()), // Configurable channel without fork
+            (1, true) => Self::set_fork_task(Some(task), channel.number()), // Configurable channel with fork
+            _ => unreachable!("{}, {}", index, channel.is_task_configurable()), // Not available with the PPI, so should not be constructable
+        }
+    }
+
+    pub(super) fn disable_task(_task: &Task, channel: &C, index: usize) {
+        match (index, channel.is_task_configurable()) {
+            (0, false) => Self::set_fork_task(None, channel.number()), // Static channel with fork
+            (0, true) => Self::set_main_task(None, channel.number()), // Configurable channel without fork
+            (1, true) => Self::set_fork_task(None, channel.number()), // Configurable channel with fork
+            _ => unreachable!(), // Not available with the PPI, so should not be constructable
+        }
+    }
+
+    pub(super) fn enable_event(event: &Event, channel: &C, _index: usize) {
+        Self::set_event(Some(event), channel.number())
+    }
+
+    pub(super) fn disable_event(_event: &Event, channel: &C, _index: usize) {
+        Self::set_event(None, channel.number())
+    }
+
+    fn set_main_task(task: Option<&Task>, channel: usize) {
+        let r = unsafe { &*pac::PPI::ptr() };
+        if let Some(task) = task {
+            r.ch[channel]
+                .tep
+                .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
+        } else {
+            r.ch[channel].tep.write(|w| unsafe { w.bits(0) })
+        }
+    }
+
+    fn set_fork_task(task: Option<&Task>, channel: usize) {
+        let r = unsafe { &*pac::PPI::ptr() };
+        if let Some(task) = task {
+            r.fork[channel]
+                .tep
+                .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
+        } else {
+            r.fork[channel].tep.write(|w| unsafe { w.bits(0) })
+        }
+    }
+
+    fn set_event(event: Option<&Event>, channel: usize) {
+        let r = unsafe { &*pac::PPI::ptr() };
+        if let Some(event) = event {
+            r.ch[channel]
+                .eep
+                .write(|w| unsafe { w.bits(event.0.as_ptr() as u32) })
+        } else {
+            r.ch[channel].eep.write(|w| unsafe { w.bits(0) })
+        }
+    }
+}
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index c21c4d39..39f3e4a4 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -31,7 +31,7 @@ pub mod gpio;
 pub mod gpiote;
 #[cfg(not(feature = "nrf9160"))]
 pub mod nvmc;
-pub mod ppi;
+pub mod interconnect;
 #[cfg(not(any(feature = "nrf52805", feature = "nrf52820")))]
 pub mod pwm;
 #[cfg(feature = "nrf52840")]
diff --git a/embassy-nrf/src/ppi.rs b/embassy-nrf/src/ppi.rs
deleted file mode 100644
index 7aaf17ee..00000000
--- a/embassy-nrf/src/ppi.rs
+++ /dev/null
@@ -1,513 +0,0 @@
-#![macro_use]
-
-//! HAL interface for the PPI peripheral.
-//!
-//! The Programmable Peripheral Interconnect interface allows for an autonomous interoperability
-//! between peripherals through their events and tasks. There are fixed PPI channels and fully
-//! configurable ones, fixed channels can only connect specific events to specific tasks. For fully
-//! configurable channels, it is possible to choose, via software, the event and the task that it
-//! will triggered by the event.
-//!
-//! On nRF52 devices, there is also a fork task endpoint, where the user can configure one more task
-//! to be triggered by the same event, even fixed PPI channels have a configurable fork task.
-
-use crate::{pac, peripherals};
-use core::marker::PhantomData;
-use core::ptr::NonNull;
-use embassy::util::Unborrow;
-use embassy_hal_common::{unborrow, unsafe_impl_unborrow};
-
-// ======================
-//       driver
-
-/// Error type of the PPI driver
-#[non_exhaustive]
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "defmt", derive(defmt::Format))]
-pub enum Error {
-    /// There is no capacity to enable this task or event (nRF51 & nRF52 only)
-    NoCapacityLeft,
-    /// This task or event is not in use by the current channel
-    NotInUseByChannel,
-    /// This task or event is already enabled on another channel (nRF53 & nRF91 only)
-    AlreadyInUse,
-}
-
-pub struct Ppi<'d, C: Channel> {
-    ch: C,
-    phantom: PhantomData<&'d mut C>,
-}
-
-impl<'d, C: Channel> Ppi<'d, C> {
-    pub fn new(ch: impl Unborrow<Target = C> + 'd) -> Self {
-        unborrow!(ch);
-
-        Self {
-            ch,
-            phantom: PhantomData,
-        }
-    }
-
-    /// Enables the channel.
-    pub fn enable(&mut self) {
-        let r = unsafe { &*pac::PPI::ptr() };
-        r.chenset
-            .write(|w| unsafe { w.bits(1 << self.ch.number()) });
-    }
-
-    /// Disables the channel.
-    pub fn disable(&mut self) {
-        let r = unsafe { &*pac::PPI::ptr() };
-        r.chenclr
-            .write(|w| unsafe { w.bits(1 << self.ch.number()) });
-    }
-}
-
-#[cfg(feature = "_ppi")]
-impl<'d, C: Channel> Ppi<'d, C> {
-    /// Makes it so that the given task is subscribed to this channel
-    pub fn subscribe(&mut self, task: Task) -> Result<(), Error> {
-        if self.is_main_task_free() {
-            self.set_main_task(Some(task));
-            Ok(())
-        } else if self.is_fork_task_free() {
-            self.set_fork_task(Some(task));
-            Ok(())
-        } else {
-            Err(Error::NoCapacityLeft)
-        }
-    }
-
-    /// Makes it so that the given task is not subscribed to this channel
-    pub fn unsubscribe(&mut self, task: Task) -> Result<(), Error> {
-        if self.get_main_task() == Some(task) {
-            // If there is a fork task, we move that to the main task for consistency
-            // If there is no fork task, then the main task is set to 0
-            let fork_task = self.get_fork_task();
-            self.set_main_task(fork_task);
-
-            if self.has_fork_task() {
-                // The fork task was copied to the main task, so reset the fork task
-                self.set_fork_task(None);
-            }
-            Ok(())
-        } else if self.get_fork_task() == Some(task) {
-            // Reset the fork task
-            self.set_fork_task(None);
-            Ok(())
-        } else {
-            Err(Error::NotInUseByChannel)
-        }
-    }
-
-    /// Makes it so that the given event is published on this channel
-    pub fn publish(&mut self, event: Event) -> Result<(), Error> {
-        if self.is_event_free() {
-            self.set_event(Some(event));
-            Ok(())
-        } else {
-            Err(Error::NoCapacityLeft)
-        }
-    }
-
-    /// Makes it so that the given event is not published on this channel
-    pub fn unpublish(&mut self, event: Event) -> Result<(), Error> {
-        if self.get_event() == Some(event) {
-            self.set_event(None);
-            Ok(())
-        } else {
-            Err(Error::NotInUseByChannel)
-        }
-    }
-
-    fn set_main_task(&mut self, task: Option<Task>) {
-        let r = unsafe { &*pac::PPI::ptr() };
-        if let Some(task) = task {
-            r.ch[self.ch.number()]
-                .tep
-                .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
-        } else {
-            r.ch[self.ch.number()].tep.write(|w| unsafe { w.bits(0) })
-        }
-    }
-
-    fn get_main_task(&mut self) -> Option<Task> {
-        let r = unsafe { &*pac::PPI::ptr() };
-
-        if !self.has_main_task() {
-            return None;
-        }
-
-        let bits = r.ch[self.ch.number()].tep.read().tep().bits();
-
-        if bits == 0 {
-            None
-        } else {
-            unsafe { Some(Task(NonNull::new_unchecked(bits as *mut _))) }
-        }
-    }
-
-    fn set_fork_task(&mut self, task: Option<Task>) {
-        let r = unsafe { &*pac::PPI::ptr() };
-        if let Some(task) = task {
-            r.fork[self.ch.number()]
-                .tep
-                .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
-        } else {
-            r.fork[self.ch.number()].tep.write(|w| unsafe { w.bits(0) })
-        }
-    }
-
-    fn get_fork_task(&mut self) -> Option<Task> {
-        let r = unsafe { &*pac::PPI::ptr() };
-
-        if !self.has_fork_task() {
-            return None;
-        }
-
-        let bits = r.fork[self.ch.number()].tep.read().tep().bits();
-
-        if bits == 0 {
-            None
-        } else {
-            unsafe { Some(Task(NonNull::new_unchecked(bits as *mut _))) }
-        }
-    }
-
-    fn has_main_task(&self) -> bool {
-        match (self.ch.task_capacity(), self.ch.event_capacity()) {
-            (0, 0) => false,     // Static task
-            (1, 0) => false,     // Static task with fork
-            (1 | 2, 1) => true,  // Configurable task with possibly a fork
-            _ => unreachable!(), // Every PPI config is covered
-        }
-    }
-
-    fn has_fork_task(&self) -> bool {
-        match (self.ch.task_capacity(), self.ch.event_capacity()) {
-            (0, 0) => false,     // Static task
-            (1, 0) => true,      // Static task with fork
-            (1, 1) => false,     // Configurable task without fork
-            (2, 1) => true,      // Configurable task with fork
-            _ => unreachable!(), // Every PPI config is covered
-        }
-    }
-
-    fn is_main_task_free(&mut self) -> bool {
-        self.get_main_task().is_none()
-    }
-
-    fn is_fork_task_free(&mut self) -> bool {
-        self.get_fork_task().is_none()
-    }
-
-    fn set_event(&mut self, event: Option<Event>) {
-        let r = unsafe { &*pac::PPI::ptr() };
-        if let Some(event) = event {
-            r.ch[self.ch.number()]
-                .eep
-                .write(|w| unsafe { w.bits(event.0.as_ptr() as u32) })
-        } else {
-            r.ch[self.ch.number()].eep.write(|w| unsafe { w.bits(0) })
-        }
-    }
-
-    fn get_event(&mut self) -> Option<Event> {
-        let r = unsafe { &*pac::PPI::ptr() };
-
-        if !self.has_event() {
-            return None;
-        }
-
-        let bits = r.ch[self.ch.number()].eep.read().eep().bits();
-
-        if bits == 0 {
-            None
-        } else {
-            unsafe { Some(Event(NonNull::new_unchecked(bits as *mut _))) }
-        }
-    }
-
-    fn has_event(&self) -> bool {
-        match (self.ch.task_capacity(), self.ch.event_capacity()) {
-            (_, 0) => false,     // Static event
-            (_, 1) => true,      // Configurable event
-            _ => unreachable!(), // Every PPI config is covered
-        }
-    }
-
-    fn is_event_free(&mut self) -> bool {
-        self.get_event().is_none()
-    }
-}
-
-#[cfg(feature = "_dppi")]
-const DPPI_ENABLE_BIT: u32 = 0x8000_0000;
-#[cfg(feature = "_dppi")]
-const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF;
-
-#[cfg(feature = "_dppi")]
-impl<'d, C: Channel> Ppi<'d, C> {
-    /// Makes it so that the given task is subscribed to this channel
-    pub fn subscribe(&mut self, task: Task) -> Result<(), Error> {
-        unsafe {
-            if Self::is_register_enabled(task.0) {
-                Err(Error::AlreadyInUse)
-            } else {
-                Self::set_register_active(task.0, self.ch.number() as u8);
-                Ok(())
-            }
-        }
-    }
-
-    /// Makes it so that the given task is not subscribed to this channel
-    pub fn unsubscribe(&mut self, task: Task) -> Result<(), Error> {
-        unsafe {
-            if Self::get_register_channel(task.0) != self.ch.number() as u8 {
-                Err(Error::NotInUseByChannel)
-            } else {
-                Self::set_register_inactive(task.0);
-                Ok(())
-            }
-        }
-    }
-
-    /// Makes it so that the given event is published on this channel
-    pub fn publish(&mut self, event: Event) -> Result<(), Error> {
-        unsafe {
-            if Self::is_register_enabled(event.0) {
-                Err(Error::AlreadyInUse)
-            } else {
-                Self::set_register_active(event.0, self.ch.number() as u8);
-                Ok(())
-            }
-        }
-    }
-
-    /// Makes it so that the given event is not published on this channel
-    pub fn unpublish(&mut self, event: Event) -> Result<(), Error> {
-        unsafe {
-            if Self::get_register_channel(event.0) != self.ch.number() as u8 {
-                Err(Error::NotInUseByChannel)
-            } else {
-                Self::set_register_inactive(event.0);
-                Ok(())
-            }
-        }
-    }
-
-    /// Checks if the DPPI_ENABLE_BIT is set in the register
-    ///
-    /// # Safety
-    ///
-    /// The register pointer must point at one of the many SUBSCRIBE_* or PUBLISH_* registers of the peripherals
-    unsafe fn is_register_enabled(register: NonNull<u32>) -> bool {
-        let bits = register.as_ptr().read_volatile();
-        bits & DPPI_ENABLE_BIT > 0
-    }
-
-    /// Sets the register to the given channel and enables it
-    ///
-    /// # Safety
-    ///
-    /// The register pointer must point at one of the many SUBSCRIBE_* or PUBLISH_* registers of the peripherals
-    unsafe fn set_register_active(register: NonNull<u32>, channel: u8) {
-        register
-            .as_ptr()
-            .write_volatile(DPPI_ENABLE_BIT | (channel as u32 & DPPI_CHANNEL_MASK));
-    }
-
-    /// Resets the channel number and disables the register
-    ///
-    /// # Safety
-    ///
-    /// The register pointer must point at one of the many SUBSCRIBE_* or PUBLISH_* registers of the peripherals
-    unsafe fn set_register_inactive(register: NonNull<u32>) {
-        register.as_ptr().write_volatile(0);
-    }
-
-    /// Gets the current configured channel number of the register
-    ///
-    /// # Safety
-    ///
-    /// The register pointer must point at one of the many SUBSCRIBE_* or PUBLISH_* registers of the peripherals
-    unsafe fn get_register_channel(register: NonNull<u32>) -> u8 {
-        let bits = register.as_ptr().read_volatile();
-        (bits & DPPI_CHANNEL_MASK) as u8
-    }
-}
-
-impl<'d, C: Channel> Drop for Ppi<'d, C> {
-    fn drop(&mut self) {
-        self.disable()
-    }
-}
-
-/// Represents a task that a peripheral can do.
-/// When a task is subscribed to a PPI channel it will run when the channel is triggered by
-/// a published event.
-///
-/// The pointer in the task can point to two different kinds of register:
-/// - PPI *(nRF51 & nRF52)*: A pointer to a task register of the task of the peripheral that has
-/// to be registered with the PPI to subscribe to a channel
-/// - DPPI *(nRF53 & nRF91)*: A pointer to the subscribe register of the task of the peripheral
-/// that has to have the channel number and enable bit written tp it to subscribe to a channel
-#[derive(PartialEq, Eq, Clone, Copy)]
-pub struct Task(pub NonNull<u32>);
-impl Task {
-    pub(crate) fn from_reg<T>(reg: &T) -> Self {
-        Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
-    }
-}
-
-/// Represents an event that a peripheral can publish.
-/// An event can be set to publish on a PPI channel when the event happens.
-///
-/// The pointer in the event can point to two different kinds of register:
-/// - PPI *(nRF51 & nRF52)*: A pointer to an event register of the event of the peripheral that has
-/// to be registered with the PPI to publish to a channel
-/// - DPPI *(nRF53 & nRF91)*: A pointer to the publish register of the event of the peripheral
-/// that has to have the channel number and enable bit written tp it to publish to a channel
-#[derive(PartialEq, Eq, Clone, Copy)]
-pub struct Event(pub NonNull<u32>);
-impl Event {
-    pub(crate) fn from_reg<T>(reg: &T) -> Self {
-        Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
-    }
-}
-
-// ======================
-//       traits
-
-pub(crate) mod sealed {
-    pub trait Channel {}
-    pub trait Group {}
-}
-
-pub trait Channel: sealed::Channel + Sized {
-    /// Returns the number of the channel
-    fn number(&self) -> usize;
-
-    /// Returns the amount of configurable tasks this channel has.
-    ///
-    /// - MAX for DPPI with unlimited capacity (nRF53 & nRF91)
-    /// - 0 for static channel without fork (nRF51)
-    /// - 1 for static channel with fork (nRF52) or for configurable channel (nRF51)
-    /// - 2 for configurable channel with fork (nRF52)
-    fn task_capacity(&self) -> usize;
-
-    /// Returns the amount of configurable events this channels has
-    ///
-    /// - MAX for DPPI with unlimited capacity (nRF53 & nRF91)
-    /// - 0 for static channel (nRF51 & nRF52)
-    /// - 1 for configurable channel (nRF51 & nRF52)
-    fn event_capacity(&self) -> usize;
-
-    fn degrade(self) -> AnyChannel {
-        pub trait ConfigurableChannel {}
-
-        AnyChannel {
-            number: self.number() as u8,
-            task_capacity: self.task_capacity() as _,
-            event_capacity: self.event_capacity() as _,
-        }
-    }
-}
-
-pub trait Group: sealed::Group + Sized {
-    fn number(&self) -> usize;
-    fn degrade(self) -> AnyGroup {
-        AnyGroup {
-            number: self.number() as u8,
-        }
-    }
-}
-
-// ======================
-//       channels
-
-pub struct AnyChannel {
-    number: u8,
-    task_capacity: u8,
-    event_capacity: u8,
-}
-unsafe_impl_unborrow!(AnyChannel);
-impl sealed::Channel for AnyChannel {}
-impl Channel for AnyChannel {
-    fn number(&self) -> usize {
-        self.number as usize
-    }
-
-    fn task_capacity(&self) -> usize {
-        self.task_capacity as _
-    }
-
-    fn event_capacity(&self) -> usize {
-        self.event_capacity as _
-    }
-}
-
-macro_rules! impl_ppi_channel {
-    ($type:ident, $number:expr, $task_capacity:expr, $event_capacity:expr) => {
-        impl crate::ppi::sealed::Channel for peripherals::$type {}
-        impl crate::ppi::Channel for peripherals::$type {
-            fn number(&self) -> usize {
-                $number
-            }
-            fn task_capacity(&self) -> usize {
-                $task_capacity
-            }
-            fn event_capacity(&self) -> usize {
-                $event_capacity
-            }
-        }
-    };
-    ($type:ident, $number:expr) => {
-        impl crate::ppi::sealed::Channel for peripherals::$type {}
-        impl crate::ppi::Channel for peripherals::$type {
-            fn number(&self) -> usize {
-                $number
-            }
-            fn task_capacity(&self) -> usize {
-                usize::MAX
-            }
-            fn event_capacity(&self) -> usize {
-                usize::MAX
-            }
-        }
-    };
-}
-
-// ======================
-//       groups
-
-pub struct AnyGroup {
-    number: u8,
-}
-unsafe_impl_unborrow!(AnyGroup);
-impl sealed::Group for AnyGroup {}
-impl Group for AnyGroup {
-    fn number(&self) -> usize {
-        self.number as usize
-    }
-}
-
-macro_rules! impl_group {
-    ($type:ident, $number:expr) => {
-        impl sealed::Group for peripherals::$type {}
-        impl Group for peripherals::$type {
-            fn number(&self) -> usize {
-                $number
-            }
-        }
-    };
-}
-
-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/timer.rs b/embassy-nrf/src/timer.rs
index e62120ae..27f8e715 100644
--- a/embassy-nrf/src/timer.rs
+++ b/embassy-nrf/src/timer.rs
@@ -11,9 +11,8 @@ use embassy_hal_common::drop::OnDrop;
 use embassy_hal_common::unborrow;
 use futures::future::poll_fn;
 
+use crate::interconnect::{Event, Task};
 use crate::pac;
-use crate::ppi::Event;
-use crate::ppi::Task;
 
 pub(crate) mod sealed {
 
@@ -184,36 +183,21 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> {
     ///
     /// When triggered, this task starts the timer.
     pub fn task_start(&self) -> Task {
-        #[cfg(feature = "_ppi")]
-        let reg = &T::regs().tasks_start;
-        #[cfg(feature = "_dppi")]
-        let reg = &T::regs().subscribe_start;
-
-        Task::from_reg(reg)
+        Task::from_reg(&T::regs().tasks_start)
     }
 
     /// Returns the STOP task, for use with PPI.
     ///
     /// When triggered, this task stops the timer.
     pub fn task_stop(&self) -> Task {
-        #[cfg(feature = "_ppi")]
-        let reg = &T::regs().tasks_stop;
-        #[cfg(feature = "_dppi")]
-        let reg = &T::regs().subscribe_stop;
-
-        Task::from_reg(reg)
+        Task::from_reg(&T::regs().tasks_stop)
     }
 
     /// Returns the CLEAR task, for use with PPI.
     ///
     /// When triggered, this task resets the timer's counter to 0.
     pub fn task_clear(&self) -> Task {
-        #[cfg(feature = "_ppi")]
-        let reg = &T::regs().tasks_clear;
-        #[cfg(feature = "_dppi")]
-        let reg = &T::regs().subscribe_clear;
-
-        Task::from_reg(reg)
+        Task::from_reg(&T::regs().tasks_clear)
     }
 
     /// Change the timer's frequency.
@@ -334,24 +318,14 @@ impl<'a, T: Instance, I: TimerType> Cc<'a, T, I> {
     ///
     /// When triggered, this task will capture the current value of the timer's counter in this register.
     pub fn task_capture(&self) -> Task {
-        #[cfg(feature = "_ppi")]
-        let reg = &T::regs().tasks_capture;
-        #[cfg(feature = "_dppi")]
-        let reg = &T::regs().subscribe_capture;
-
-        Task::from_reg(reg)
+        Task::from_reg(&T::regs().tasks_capture)
     }
 
     /// Returns this CC register's COMPARE event, for use with PPI.
     ///
     /// This event will fire when the timer's counter reaches the value in this CC register.
     pub fn event_compare(&self) -> Event {
-        #[cfg(feature = "_ppi")]
-        let reg = &T::regs().events_compare[self.n];
-        #[cfg(feature = "_dppi")]
-        let reg = &T::regs().publish_compare[self.n];
-
-        Event::from_reg(reg)
+        Event::from_reg(&T::regs().events_compare[self.n])
     }
 
     /// Enable the shortcut between this CC register's COMPARE event and the timer's CLEAR task.
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs
index 71401a56..17e60ce2 100644
--- a/embassy-nrf/src/uarte.rs
+++ b/embassy-nrf/src/uarte.rs
@@ -16,28 +16,15 @@ use futures::future::poll_fn;
 use crate::chip::EASY_DMA_SIZE;
 use crate::gpio::sealed::Pin as _;
 use crate::gpio::{self, OptionalPin as GpioOptionalPin, Pin as GpioPin};
+use crate::interconnect::{AnyChannel, Event, OneToOneChannel, OneToTwoChannel, Ppi, Task};
 use crate::interrupt::Interrupt;
 use crate::pac;
-use crate::ppi::{AnyChannel, Channel, Event, Ppi, Task};
 use crate::timer::Instance as TimerInstance;
 use crate::timer::{Frequency, Timer};
 
 // Re-export SVD variants to allow user to directly set values.
 pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
 
-#[non_exhaustive]
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "defmt", derive(defmt::Format))]
-pub enum Error {
-    PpiError(crate::ppi::Error),
-}
-
-impl From<crate::ppi::Error> for Error {
-    fn from(e: crate::ppi::Error) -> Self {
-        Self::PpiError(e)
-    }
-}
-
 #[non_exhaustive]
 pub struct Config {
     pub parity: Parity,
@@ -344,17 +331,14 @@ impl<'d, T: Instance> Write for Uarte<'d, T> {
 pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> {
     uarte: Uarte<'d, U>,
     timer: Timer<'d, T>,
-    ppi_ch1: Ppi<'d, AnyChannel>,
-    _ppi_ch2: Ppi<'d, AnyChannel>,
+    ppi_ch1: Ppi<'d, AnyChannel, 1, 2>,
+    _ppi_ch2: Ppi<'d, AnyChannel, 1, 1>,
 }
 
 impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
     /// Creates the interface to a UARTE instance.
     /// Sets the baud rate, parity and assigns the pins to the UARTE peripheral.
     ///
-    /// - *Note:* ppi_ch1 must have at least 1 free event and 2 free tasks or a PPI error is returned
-    /// - *Note:* ppi_ch2 must have at least 1 free event and 1 free tasks or a PPI error is returned
-    ///
     /// # Safety
     ///
     /// The returned API is safe unless you use `mem::forget` (or similar safe mechanisms)
@@ -364,15 +348,15 @@ impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
     pub unsafe fn new(
         uarte: impl Unborrow<Target = U> + 'd,
         timer: impl Unborrow<Target = T> + 'd,
-        ppi_ch1: impl Unborrow<Target = impl Channel> + 'd,
-        ppi_ch2: impl Unborrow<Target = impl Channel> + 'd,
+        ppi_ch1: impl Unborrow<Target = impl OneToTwoChannel + 'd> + 'd,
+        ppi_ch2: impl Unborrow<Target = impl OneToOneChannel + 'd> + 'd,
         irq: impl Unborrow<Target = U::Interrupt> + 'd,
         rxd: impl Unborrow<Target = impl GpioPin> + 'd,
         txd: impl Unborrow<Target = impl GpioPin> + 'd,
         cts: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
         rts: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
         config: Config,
-    ) -> Result<Self, Error> {
+    ) -> Self {
         let baudrate = config.baudrate;
         let uarte = Uarte::new(uarte, irq, rxd, txd, cts, rts, config);
         let mut timer = Timer::new(timer);
@@ -394,23 +378,29 @@ impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
         timer.cc(0).short_compare_clear();
         timer.cc(0).short_compare_stop();
 
-        let mut ppi_ch1 = Ppi::new(ppi_ch1.degrade());
-        ppi_ch1.publish(Event::from_reg(&r.events_rxdrdy))?;
-        ppi_ch1.subscribe(timer.task_clear())?;
-        ppi_ch1.subscribe(timer.task_start())?;
+        let mut ppi_ch1 = Ppi::new_one_to_two(
+            ppi_ch1,
+            Event::from_reg(&r.events_rxdrdy),
+            timer.task_clear(),
+            timer.task_start(),
+        )
+        .degrade();
         ppi_ch1.enable();
 
-        let mut ppi_ch2 = Ppi::new(ppi_ch2.degrade());
-        ppi_ch2.publish(timer.cc(0).event_compare())?;
-        ppi_ch2.subscribe(Task::from_reg(&r.tasks_stoprx))?;
+        let mut ppi_ch2 = Ppi::new_one_to_one(
+            ppi_ch2,
+            timer.cc(0).event_compare(),
+            Task::from_reg(&r.tasks_stoprx),
+        )
+        .degrade();
         ppi_ch2.enable();
 
-        Ok(Self {
+        Self {
             uarte,
             timer,
             ppi_ch1: ppi_ch1,
             _ppi_ch2: ppi_ch2,
-        })
+        }
     }
 }
 
diff --git a/examples/nrf/src/bin/buffered_uart.rs b/examples/nrf/src/bin/buffered_uart.rs
index b01c83ce..69c7de93 100644
--- a/examples/nrf/src/bin/buffered_uart.rs
+++ b/examples/nrf/src/bin/buffered_uart.rs
@@ -26,7 +26,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
     let irq = interrupt::take!(UARTE0_UART0);
     let mut state = State::new();
     let u = unsafe {
-        unwrap!(BufferedUarte::new(
+        BufferedUarte::new(
             &mut state,
             p.UARTE0,
             p.TIMER0,
@@ -40,7 +40,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
             config,
             &mut rx_buffer,
             &mut tx_buffer,
-        ))
+        )
     };
     pin_mut!(u);
 
diff --git a/examples/nrf/src/bin/ppi.rs b/examples/nrf/src/bin/ppi.rs
index 55089396..4edb5d7c 100644
--- a/examples/nrf/src/bin/ppi.rs
+++ b/examples/nrf/src/bin/ppi.rs
@@ -10,7 +10,7 @@ use core::future::pending;
 use embassy::executor::Spawner;
 use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};
 use embassy_nrf::gpiote::{self, InputChannel, InputChannelPolarity};
-use embassy_nrf::ppi::Ppi;
+use embassy_nrf::interconnect::Ppi;
 use embassy_nrf::Peripherals;
 use gpiote::{OutputChannel, OutputChannelPolarity};
 
@@ -51,25 +51,21 @@ async fn main(_spawner: Spawner, p: Peripherals) {
         OutputChannelPolarity::Toggle,
     );
 
-    let mut ppi = Ppi::new(p.PPI_CH0);
-    ppi.publish(button1.event_in()).unwrap();
-    ppi.subscribe(led1.task_out()).unwrap();
+    let mut ppi = Ppi::new_one_to_one(p.PPI_CH0, button1.event_in(), led1.task_out());
     ppi.enable();
 
-    let mut ppi = Ppi::new(p.PPI_CH1);
-    ppi.publish(button2.event_in()).unwrap();
-    ppi.subscribe(led1.task_clr()).unwrap();
+    let mut ppi = Ppi::new_one_to_one(p.PPI_CH1, button2.event_in(), led1.task_clr());
     ppi.enable();
 
-    let mut ppi = Ppi::new(p.PPI_CH2);
-    ppi.publish(button3.event_in()).unwrap();
-    ppi.subscribe(led1.task_set()).unwrap();
+    let mut ppi = Ppi::new_one_to_one(p.PPI_CH2, button3.event_in(), led1.task_set());
     ppi.enable();
 
-    let mut ppi = Ppi::new(p.PPI_CH3);
-    ppi.publish(button4.event_in()).unwrap();
-    ppi.subscribe(led1.task_out()).unwrap();
-    ppi.subscribe(led2.task_out()).unwrap();
+    let mut ppi = Ppi::new_one_to_two(
+        p.PPI_CH3,
+        button4.event_in(),
+        led1.task_out(),
+        led2.task_out(),
+    );
     ppi.enable();
 
     info!("PPI setup!");
diff --git a/examples/nrf/src/bin/uart_idle.rs b/examples/nrf/src/bin/uart_idle.rs
index e04e5cf0..e9f4a285 100644
--- a/examples/nrf/src/bin/uart_idle.rs
+++ b/examples/nrf/src/bin/uart_idle.rs
@@ -21,9 +21,9 @@ async fn main(_spawner: Spawner, p: Peripherals) {
 
     let irq = interrupt::take!(UARTE0_UART0);
     let mut uart = unsafe {
-        unwrap!(uarte::UarteWithIdle::new(
+        uarte::UarteWithIdle::new(
             p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, irq, p.P0_08, p.P0_06, NoPin, NoPin, config,
-        ))
+        )
     };
 
     info!("uarte initialized!");

From 531dfcffb3c203dff15dcb53fe25577c121c7784 Mon Sep 17 00:00:00 2001
From: Dion Dokter <dion@tweedegolf.com>
Date: Mon, 18 Oct 2021 16:26:27 +0200
Subject: [PATCH 05/10] fmt

---
 embassy-nrf/src/interconnect/mod.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/embassy-nrf/src/interconnect/mod.rs b/embassy-nrf/src/interconnect/mod.rs
index 23cc4527..8cb505bd 100644
--- a/embassy-nrf/src/interconnect/mod.rs
+++ b/embassy-nrf/src/interconnect/mod.rs
@@ -10,10 +10,10 @@
 //!
 //! On nRF52 devices, there is also a fork task endpoint, where the user can configure one more task
 //! to be triggered by the same event, even fixed PPI channels have a configurable fork task.
-//! 
+//!
 //! The DPPI for nRF53 and nRF91 devices works in a different way. Every channel can support infinitely
 //! many tasks and events, but any single task or event can only be coupled with one channel.
-//! 
+//!
 
 use crate::{pac, peripherals};
 use core::marker::PhantomData;

From a6c84cb91552fc0442a28126d3fae60031aa6622 Mon Sep 17 00:00:00 2001
From: Dion Dokter <dion@tweedegolf.com>
Date: Tue, 19 Oct 2021 10:13:08 +0200
Subject: [PATCH 06/10] - Interconnect is now PPI again - Scary pointer math is
 now contained in the tasks and events - ppi now sets the tasks and events
 immediately and the struct is now zero-sized - StaticToOne is renamed to
 ZeroToOne - Used DPPI tasks and events now panic when enabled twice

---
 embassy-nrf/src/buffered_uarte.rs            |   2 +-
 embassy-nrf/src/gpiote.rs                    |   2 +-
 embassy-nrf/src/interconnect/dppi.rs         |  47 -------
 embassy-nrf/src/interconnect/ppi.rs          |  65 ----------
 embassy-nrf/src/lib.rs                       |   2 +-
 embassy-nrf/src/ppi/dppi.rs                  |  61 +++++++++
 embassy-nrf/src/{interconnect => ppi}/mod.rs | 125 ++++++++++---------
 embassy-nrf/src/ppi/ppi.rs                   |  77 ++++++++++++
 embassy-nrf/src/timer.rs                     |   2 +-
 embassy-nrf/src/uarte.rs                     |   2 +-
 10 files changed, 209 insertions(+), 176 deletions(-)
 delete mode 100644 embassy-nrf/src/interconnect/dppi.rs
 delete mode 100644 embassy-nrf/src/interconnect/ppi.rs
 create mode 100644 embassy-nrf/src/ppi/dppi.rs
 rename embassy-nrf/src/{interconnect => ppi}/mod.rs (79%)
 create mode 100644 embassy-nrf/src/ppi/ppi.rs

diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs
index 7c9a2344..1dc04f4f 100644
--- a/embassy-nrf/src/buffered_uarte.rs
+++ b/embassy-nrf/src/buffered_uarte.rs
@@ -14,8 +14,8 @@ use embassy_hal_common::{low_power_wait_until, unborrow};
 
 use crate::gpio::sealed::Pin as _;
 use crate::gpio::{OptionalPin as GpioOptionalPin, Pin as GpioPin};
-use crate::interconnect::{AnyChannel, Event, OneToOneChannel, OneToTwoChannel, Ppi, Task};
 use crate::pac;
+use crate::ppi::{AnyChannel, Event, OneToOneChannel, OneToTwoChannel, Ppi, Task};
 use crate::timer::Instance as TimerInstance;
 use crate::timer::{Frequency, Timer};
 use crate::uarte::{Config, Instance as UarteInstance};
diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs
index 6703bfc6..7ec072ac 100644
--- a/embassy-nrf/src/gpiote.rs
+++ b/embassy-nrf/src/gpiote.rs
@@ -11,8 +11,8 @@ use futures::future::poll_fn;
 
 use crate::gpio::sealed::Pin as _;
 use crate::gpio::{AnyPin, Input, Output, Pin as GpioPin};
-use crate::interconnect::{Event, Task};
 use crate::pac;
+use crate::ppi::{Event, Task};
 use crate::{interrupt, peripherals};
 
 pub const CHANNEL_COUNT: usize = 8;
diff --git a/embassy-nrf/src/interconnect/dppi.rs b/embassy-nrf/src/interconnect/dppi.rs
deleted file mode 100644
index 60f19fca..00000000
--- a/embassy-nrf/src/interconnect/dppi.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use super::{Channel, Event, Ppi, Task};
-
-const DPPI_ENABLE_BIT: u32 = 0x8000_0000;
-const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF;
-const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
-
-impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
-    Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
-{
-    pub(super) fn enable_task(task: &Task, channel: &C, _index: usize) {
-        unsafe {
-            task.0
-                .as_ptr()
-                .add(REGISTER_DPPI_CONFIG_OFFSET)
-                .write_volatile(DPPI_ENABLE_BIT | (channel.number() as u32 & DPPI_CHANNEL_MASK));
-        }
-    }
-
-    pub(super) fn disable_task(task: &Task, _channel: &C, _index: usize) {
-        unsafe {
-            task.0
-                .as_ptr()
-                .add(REGISTER_DPPI_CONFIG_OFFSET)
-                .write_volatile(0);
-        }
-    }
-
-    pub(super) fn enable_event(event: &Event, channel: &C, _index: usize) {
-        unsafe {
-            event
-                .0
-                .as_ptr()
-                .add(REGISTER_DPPI_CONFIG_OFFSET)
-                .write_volatile(DPPI_ENABLE_BIT | (channel.number() as u32 & DPPI_CHANNEL_MASK));
-        }
-    }
-
-    pub(super) fn disable_event(event: &Event, _channel: &C, _index: usize) {
-        unsafe {
-            event
-                .0
-                .as_ptr()
-                .add(REGISTER_DPPI_CONFIG_OFFSET)
-                .write_volatile(0);
-        }
-    }
-}
diff --git a/embassy-nrf/src/interconnect/ppi.rs b/embassy-nrf/src/interconnect/ppi.rs
deleted file mode 100644
index 91ff811f..00000000
--- a/embassy-nrf/src/interconnect/ppi.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-use super::{Channel, Event, Ppi, Task};
-use crate::pac;
-
-impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
-    Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
-{
-    pub(super) fn enable_task(task: &Task, channel: &C, index: usize) {
-        match (index, channel.is_task_configurable()) {
-            (0, false) => Self::set_fork_task(Some(task), channel.number()), // Static channel with fork
-            (0, true) => Self::set_main_task(Some(task), channel.number()), // Configurable channel without fork
-            (1, true) => Self::set_fork_task(Some(task), channel.number()), // Configurable channel with fork
-            _ => unreachable!("{}, {}", index, channel.is_task_configurable()), // Not available with the PPI, so should not be constructable
-        }
-    }
-
-    pub(super) fn disable_task(_task: &Task, channel: &C, index: usize) {
-        match (index, channel.is_task_configurable()) {
-            (0, false) => Self::set_fork_task(None, channel.number()), // Static channel with fork
-            (0, true) => Self::set_main_task(None, channel.number()), // Configurable channel without fork
-            (1, true) => Self::set_fork_task(None, channel.number()), // Configurable channel with fork
-            _ => unreachable!(), // Not available with the PPI, so should not be constructable
-        }
-    }
-
-    pub(super) fn enable_event(event: &Event, channel: &C, _index: usize) {
-        Self::set_event(Some(event), channel.number())
-    }
-
-    pub(super) fn disable_event(_event: &Event, channel: &C, _index: usize) {
-        Self::set_event(None, channel.number())
-    }
-
-    fn set_main_task(task: Option<&Task>, channel: usize) {
-        let r = unsafe { &*pac::PPI::ptr() };
-        if let Some(task) = task {
-            r.ch[channel]
-                .tep
-                .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
-        } else {
-            r.ch[channel].tep.write(|w| unsafe { w.bits(0) })
-        }
-    }
-
-    fn set_fork_task(task: Option<&Task>, channel: usize) {
-        let r = unsafe { &*pac::PPI::ptr() };
-        if let Some(task) = task {
-            r.fork[channel]
-                .tep
-                .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
-        } else {
-            r.fork[channel].tep.write(|w| unsafe { w.bits(0) })
-        }
-    }
-
-    fn set_event(event: Option<&Event>, channel: usize) {
-        let r = unsafe { &*pac::PPI::ptr() };
-        if let Some(event) = event {
-            r.ch[channel]
-                .eep
-                .write(|w| unsafe { w.bits(event.0.as_ptr() as u32) })
-        } else {
-            r.ch[channel].eep.write(|w| unsafe { w.bits(0) })
-        }
-    }
-}
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 39f3e4a4..c21c4d39 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -31,7 +31,7 @@ pub mod gpio;
 pub mod gpiote;
 #[cfg(not(feature = "nrf9160"))]
 pub mod nvmc;
-pub mod interconnect;
+pub mod ppi;
 #[cfg(not(any(feature = "nrf52805", feature = "nrf52820")))]
 pub mod pwm;
 #[cfg(feature = "nrf52840")]
diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs
new file mode 100644
index 00000000..083ec858
--- /dev/null
+++ b/embassy-nrf/src/ppi/dppi.rs
@@ -0,0 +1,61 @@
+use super::{Channel, Event, Ppi, Task};
+
+const DPPI_ENABLE_BIT: u32 = 0x8000_0000;
+const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF;
+
+impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
+    Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
+{
+    pub(super) fn enable_task(task: &Task, channel: &C, _index: usize) {
+        unsafe {
+            if task.subscribe_reg().read_volatile() != 0 {
+                panic!("Task is already in use");
+            }
+            task.subscribe_reg()
+                .write_volatile(DPPI_ENABLE_BIT | (channel.number() as u32 & DPPI_CHANNEL_MASK));
+        }
+    }
+
+    pub(super) fn disable_task(task: &Task, _channel: &C, _index: usize) {
+        unsafe {
+            task.subscribe_reg().write_volatile(0);
+        }
+    }
+
+    pub(super) fn enable_event(event: &Event, channel: &C, _index: usize) {
+        unsafe {
+            if event.publish_reg().read_volatile() != 0 {
+                panic!("Task is already in use");
+            }
+            event
+                .publish_reg()
+                .write_volatile(DPPI_ENABLE_BIT | (channel.number() as u32 & DPPI_CHANNEL_MASK));
+        }
+    }
+
+    pub(super) fn disable_event(event: &Event, _channel: &C, _index: usize) {
+        unsafe {
+            event.publish_reg().write_volatile(0);
+        }
+    }
+
+    /// Enables all tasks and events
+    pub(super) fn enable_all(tasks: &[Task], events: &[Event], channel: &C) {
+        for (index, task) in tasks.iter().enumerate() {
+            Self::enable_task(task, channel, index);
+        }
+        for (index, event) in events.iter().enumerate() {
+            Self::enable_event(event, channel, index);
+        }
+    }
+
+    /// Disable all tasks and events
+    pub(super) fn disable_all(&self) {
+        for (index, task) in self.tasks.iter().enumerate() {
+            Self::disable_task(task, &self.ch, index);
+        }
+        for (index, event) in self.events.iter().enumerate() {
+            Self::disable_event(event, &self.ch, index);
+        }
+    }
+}
diff --git a/embassy-nrf/src/interconnect/mod.rs b/embassy-nrf/src/ppi/mod.rs
similarity index 79%
rename from embassy-nrf/src/interconnect/mod.rs
rename to embassy-nrf/src/ppi/mod.rs
index 8cb505bd..ffb6af02 100644
--- a/embassy-nrf/src/interconnect/mod.rs
+++ b/embassy-nrf/src/ppi/mod.rs
@@ -28,12 +28,14 @@ mod ppi;
 
 pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
     ch: C,
+    #[cfg(feature = "_dppi")]
     events: [Event; EVENT_COUNT],
+    #[cfg(feature = "_dppi")]
     tasks: [Task; TASK_COUNT],
     phantom: PhantomData<&'d mut C>,
 }
 
-impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
+impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
     Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
 {
     pub fn degrade(self) -> Ppi<'d, AnyChannel, EVENT_COUNT, TASK_COUNT> {
@@ -43,7 +45,9 @@ impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
                 #[cfg(feature = "_ppi")]
                 has_configurable_task: self.ch.is_task_configurable(),
             },
+            #[cfg(feature = "_dppi")]
             events: self.events,
+            #[cfg(feature = "_dppi")]
             tasks: self.tasks,
             phantom: PhantomData,
         }
@@ -62,26 +66,6 @@ impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
         r.chenclr
             .write(|w| unsafe { w.bits(1 << self.ch.number()) });
     }
-
-    /// Enables all tasks and events
-    fn enable_all(&self) {
-        for (index, task) in self.tasks.iter().enumerate() {
-            Self::enable_task(task, &self.ch, index);
-        }
-        for (index, event) in self.events.iter().enumerate() {
-            Self::enable_event(event, &self.ch, index);
-        }
-    }
-
-    /// Disable all tasks and events
-    fn disable_all(&self) {
-        for (index, task) in self.tasks.iter().enumerate() {
-            Self::disable_task(task, &self.ch, index);
-        }
-        for (index, event) in self.events.iter().enumerate() {
-            Self::disable_event(event, &self.ch, index);
-        }
-    }
 }
 
 impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop
@@ -93,19 +77,23 @@ impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop
     }
 }
 
-impl<'d, C: StaticToOneChannel> Ppi<'d, C, 0, 1> {
+impl<'d, C: ZeroToOneChannel> Ppi<'d, C, 0, 1> {
     pub fn new_static_to_one(ch: impl Unborrow<Target = C> + 'd, task: Task) -> Self {
         unborrow!(ch);
 
-        let s = Self {
-            ch,
-            events: [],
-            tasks: [task],
-            phantom: PhantomData,
-        };
+        let events = [];
+        let tasks = [task];
 
-        s.enable_all();
-        s
+        Self::enable_all(&tasks, &events, &ch);
+
+        Self {
+            ch,
+            #[cfg(feature = "_dppi")]
+            events,
+            #[cfg(feature = "_dppi")]
+            tasks,
+            phantom: PhantomData,
+        }
     }
 }
 
@@ -113,15 +101,19 @@ impl<'d, C: OneToOneChannel> Ppi<'d, C, 1, 1> {
     pub fn new_one_to_one(ch: impl Unborrow<Target = C> + 'd, event: Event, task: Task) -> Self {
         unborrow!(ch);
 
-        let s = Self {
-            ch,
-            events: [event],
-            tasks: [task],
-            phantom: PhantomData,
-        };
+        let events = [event];
+        let tasks = [task];
 
-        s.enable_all();
-        s
+        Self::enable_all(&tasks, &events, &ch);
+
+        Self {
+            ch,
+            #[cfg(feature = "_dppi")]
+            events,
+            #[cfg(feature = "_dppi")]
+            tasks,
+            phantom: PhantomData,
+        }
     }
 }
 
@@ -134,15 +126,19 @@ impl<'d, C: OneToTwoChannel> Ppi<'d, C, 1, 2> {
     ) -> Self {
         unborrow!(ch);
 
-        let s = Self {
-            ch,
-            events: [event],
-            tasks: [task1, task2],
-            phantom: PhantomData,
-        };
+        let events = [event];
+        let tasks = [task1, task2];
 
-        s.enable_all();
-        s
+        Self::enable_all(&tasks, &events, &ch);
+
+        Self {
+            ch,
+            #[cfg(feature = "_dppi")]
+            events,
+            #[cfg(feature = "_dppi")]
+            tasks,
+            phantom: PhantomData,
+        }
     }
 }
 
@@ -156,18 +152,21 @@ impl<'d, C: ManyToManyChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize
     ) -> Self {
         unborrow!(ch);
 
-        let s = Self {
+        Self::enable_all(&tasks, &events, &ch);
+
+        Self {
             ch,
+            #[cfg(feature = "_dppi")]
             events,
+            #[cfg(feature = "_dppi")]
             tasks,
             phantom: PhantomData,
-        };
-
-        s.enable_all();
-        s
+        }
     }
 }
 
+const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
+
 /// Represents a task that a peripheral can do.
 /// When a task is subscribed to a PPI channel it will run when the channel is triggered by
 /// a published event.
@@ -179,6 +178,10 @@ impl Task {
     pub(crate) fn from_reg<T>(reg: &T) -> Self {
         Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
     }
+
+    pub fn subscribe_reg(&self) -> *mut u32 {
+        unsafe { self.0.as_ptr().add(REGISTER_DPPI_CONFIG_OFFSET) }
+    }
 }
 
 /// # Safety
@@ -196,6 +199,10 @@ impl Event {
     pub(crate) fn from_reg<T>(reg: &T) -> Self {
         Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
     }
+
+    pub fn publish_reg(&self) -> *mut u32 {
+        unsafe { self.0.as_ptr().add(REGISTER_DPPI_CONFIG_OFFSET) }
+    }
 }
 
 /// # Safety
@@ -218,8 +225,8 @@ pub trait Channel: sealed::Channel + Unborrow<Target = Self> + Sized {
     fn is_task_configurable(&self) -> bool;
 }
 
-pub trait StaticToOneChannel: Channel {}
-pub trait OneToOneChannel: StaticToOneChannel {}
+pub trait ZeroToOneChannel: Channel {}
+pub trait OneToOneChannel: ZeroToOneChannel {}
 pub trait OneToTwoChannel: OneToOneChannel {}
 pub trait ManyToManyChannel: OneToTwoChannel {}
 
@@ -250,8 +257,8 @@ impl Channel for AnyChannel {
 
 macro_rules! impl_ppi_channel {
     ($type:ident, $number:expr, $has_configurable_task:expr) => {
-        impl crate::interconnect::sealed::Channel for peripherals::$type {}
-        impl crate::interconnect::Channel for peripherals::$type {
+        impl crate::ppi::sealed::Channel for peripherals::$type {}
+        impl crate::ppi::Channel for peripherals::$type {
             fn number(&self) -> usize {
                 $number
             }
@@ -267,19 +274,19 @@ macro_rules! impl_ppi_channel {
     };
     ($type:ident, $number:expr, $has_configurable_task:expr, 0, 1) => {
         impl_ppi_channel!($type, $number, $has_configurable_task, 0, 0);
-        impl crate::interconnect::StaticToOneChannel for peripherals::$type {}
+        impl crate::ppi::ZeroToOneChannel for peripherals::$type {}
     };
     ($type:ident, $number:expr, $has_configurable_task:expr, 1, 1) => {
         impl_ppi_channel!($type, $number, $has_configurable_task, 0, 1);
-        impl crate::interconnect::OneToOneChannel for peripherals::$type {}
+        impl crate::ppi::OneToOneChannel for peripherals::$type {}
     };
     ($type:ident, $number:expr, $has_configurable_task:expr, 1, 2) => {
         impl_ppi_channel!($type, $number, $has_configurable_task, 1, 1);
-        impl crate::interconnect::OneToTwoChannel for peripherals::$type {}
+        impl crate::ppi::OneToTwoChannel for peripherals::$type {}
     };
     ($type:ident, $number:expr, $has_configurable_task:expr, many, many) => {
         impl_ppi_channel!($type, $number, $has_configurable_task, 1, 2);
-        impl crate::interconnect::ManyToManyChannel for peripherals::$type {}
+        impl crate::ppi::ManyToManyChannel for peripherals::$type {}
     };
 }
 
diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs
new file mode 100644
index 00000000..67d2086c
--- /dev/null
+++ b/embassy-nrf/src/ppi/ppi.rs
@@ -0,0 +1,77 @@
+use super::{Channel, Event, Ppi, Task};
+use crate::pac;
+
+impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
+    Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
+{
+    fn set_main_task(task: Option<&Task>, channel: usize) {
+        let r = unsafe { &*pac::PPI::ptr() };
+        if let Some(task) = task {
+            r.ch[channel]
+                .tep
+                .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
+        } else {
+            r.ch[channel].tep.write(|w| unsafe { w.bits(0) })
+        }
+    }
+
+    fn set_fork_task(task: Option<&Task>, channel: usize) {
+        let r = unsafe { &*pac::PPI::ptr() };
+        if let Some(task) = task {
+            r.fork[channel]
+                .tep
+                .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
+        } else {
+            r.fork[channel].tep.write(|w| unsafe { w.bits(0) })
+        }
+    }
+
+    fn set_event(event: Option<&Event>, channel: usize) {
+        let r = unsafe { &*pac::PPI::ptr() };
+        if let Some(event) = event {
+            r.ch[channel]
+                .eep
+                .write(|w| unsafe { w.bits(event.0.as_ptr() as u32) })
+        } else {
+            r.ch[channel].eep.write(|w| unsafe { w.bits(0) })
+        }
+    }
+
+    /// Enables all tasks and events
+    pub(super) fn enable_all(tasks: &[Task], events: &[Event], channel: &C) {
+        // One configurable task, no fork
+        if channel.is_task_configurable() && TASK_COUNT == 1 {
+            Self::set_main_task(Some(&tasks[0]), channel.number());
+        }
+
+        // One configurable task, as fork
+        if !channel.is_task_configurable() && TASK_COUNT == 1 {
+            Self::set_fork_task(Some(&tasks[0]), channel.number());
+        }
+
+        // Two configurable tasks (main + fork)
+        if TASK_COUNT == 2 {
+            Self::set_main_task(Some(&tasks[0]), channel.number());
+            Self::set_fork_task(Some(&tasks[1]), channel.number());
+        }
+
+        if EVENT_COUNT == 1 {
+            Self::set_event(Some(&events[0]), channel.number());
+        }
+    }
+
+    /// Disable all tasks and events
+    pub(super) fn disable_all(&self) {
+        if self.ch.is_task_configurable() {
+            Self::set_main_task(None, self.ch.number());
+        }
+
+        if TASK_COUNT == 1 && !self.ch.is_task_configurable() || TASK_COUNT == 2 {
+            Self::set_fork_task(None, self.ch.number());
+        }
+
+        if EVENT_COUNT == 1 {
+            Self::set_event(None, self.ch.number());
+        }
+    }
+}
diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs
index 27f8e715..9173338b 100644
--- a/embassy-nrf/src/timer.rs
+++ b/embassy-nrf/src/timer.rs
@@ -11,8 +11,8 @@ use embassy_hal_common::drop::OnDrop;
 use embassy_hal_common::unborrow;
 use futures::future::poll_fn;
 
-use crate::interconnect::{Event, Task};
 use crate::pac;
+use crate::ppi::{Event, Task};
 
 pub(crate) mod sealed {
 
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs
index 17e60ce2..36cf65d8 100644
--- a/embassy-nrf/src/uarte.rs
+++ b/embassy-nrf/src/uarte.rs
@@ -16,9 +16,9 @@ use futures::future::poll_fn;
 use crate::chip::EASY_DMA_SIZE;
 use crate::gpio::sealed::Pin as _;
 use crate::gpio::{self, OptionalPin as GpioOptionalPin, Pin as GpioPin};
-use crate::interconnect::{AnyChannel, Event, OneToOneChannel, OneToTwoChannel, Ppi, Task};
 use crate::interrupt::Interrupt;
 use crate::pac;
+use crate::ppi::{AnyChannel, Event, OneToOneChannel, OneToTwoChannel, Ppi, Task};
 use crate::timer::Instance as TimerInstance;
 use crate::timer::{Frequency, Timer};
 

From 6205d6da478be0392beaaee27e2f48e8e223a6ca Mon Sep 17 00:00:00 2001
From: Dion Dokter <dion@tweedegolf.com>
Date: Tue, 19 Oct 2021 10:20:22 +0200
Subject: [PATCH 07/10] typo

---
 embassy-nrf/src/ppi/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs
index ffb6af02..bfbc69b7 100644
--- a/embassy-nrf/src/ppi/mod.rs
+++ b/embassy-nrf/src/ppi/mod.rs
@@ -78,7 +78,7 @@ impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop
 }
 
 impl<'d, C: ZeroToOneChannel> Ppi<'d, C, 0, 1> {
-    pub fn new_static_to_one(ch: impl Unborrow<Target = C> + 'd, task: Task) -> Self {
+    pub fn new_zero_to_one(ch: impl Unborrow<Target = C> + 'd, task: Task) -> Self {
         unborrow!(ch);
 
         let events = [];

From 4d3341dbb980131762d714cb5b5d8dfb11060119 Mon Sep 17 00:00:00 2001
From: Dion Dokter <dion@tweedegolf.com>
Date: Tue, 19 Oct 2021 10:27:49 +0200
Subject: [PATCH 08/10] Fixed examples

---
 examples/nrf/src/bin/ppi.rs              | 2 +-
 examples/nrf/src/bin/saadc_continuous.rs | 4 +---
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/examples/nrf/src/bin/ppi.rs b/examples/nrf/src/bin/ppi.rs
index 4edb5d7c..99246eed 100644
--- a/examples/nrf/src/bin/ppi.rs
+++ b/examples/nrf/src/bin/ppi.rs
@@ -10,7 +10,7 @@ use core::future::pending;
 use embassy::executor::Spawner;
 use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};
 use embassy_nrf::gpiote::{self, InputChannel, InputChannelPolarity};
-use embassy_nrf::interconnect::Ppi;
+use embassy_nrf::ppi::Ppi;
 use embassy_nrf::Peripherals;
 use gpiote::{OutputChannel, OutputChannelPolarity};
 
diff --git a/examples/nrf/src/bin/saadc_continuous.rs b/examples/nrf/src/bin/saadc_continuous.rs
index 149b9c60..a06a01e9 100644
--- a/examples/nrf/src/bin/saadc_continuous.rs
+++ b/examples/nrf/src/bin/saadc_continuous.rs
@@ -32,9 +32,7 @@ async fn main(_spawner: Spawner, mut p: Peripherals) {
     timer.cc(0).write(100); // We want to sample at 10KHz
     timer.cc(0).short_compare_clear();
 
-    let mut ppi = Ppi::new(p.PPI_CH0);
-    ppi.set_event(timer.cc(0).event_compare());
-    ppi.set_task(saadc.task_sample());
+    let mut ppi = Ppi::new_one_to_one(p.PPI_CH0, timer.cc(0).event_compare(), saadc.task_sample());
     ppi.enable();
 
     timer.start();

From c63d74720980fcf9acbf5b1d8adbb9dc2031d391 Mon Sep 17 00:00:00 2001
From: Dion Dokter <dion@tweedegolf.com>
Date: Tue, 26 Oct 2021 09:45:29 +0200
Subject: [PATCH 09/10] Fewer channel traits, more cfg to make the system work

---
 embassy-nrf/src/buffered_uarte.rs |  20 ++---
 embassy-nrf/src/chips/nrf52805.rs |  44 +++++-----
 embassy-nrf/src/chips/nrf52810.rs |  64 +++++++-------
 embassy-nrf/src/chips/nrf52811.rs |  64 +++++++-------
 embassy-nrf/src/chips/nrf52820.rs |  64 +++++++-------
 embassy-nrf/src/chips/nrf52832.rs |  64 +++++++-------
 embassy-nrf/src/chips/nrf52833.rs |  64 +++++++-------
 embassy-nrf/src/chips/nrf52840.rs |  64 +++++++-------
 embassy-nrf/src/chips/nrf9160.rs  |  32 +++----
 embassy-nrf/src/ppi/mod.rs        | 135 +++++++++++++++++-------------
 embassy-nrf/src/ppi/ppi.rs        |  12 ++-
 embassy-nrf/src/uarte.rs          |  20 ++---
 12 files changed, 335 insertions(+), 312 deletions(-)

diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs
index 1dc04f4f..717ada78 100644
--- a/embassy-nrf/src/buffered_uarte.rs
+++ b/embassy-nrf/src/buffered_uarte.rs
@@ -15,7 +15,7 @@ use embassy_hal_common::{low_power_wait_until, unborrow};
 use crate::gpio::sealed::Pin as _;
 use crate::gpio::{OptionalPin as GpioOptionalPin, Pin as GpioPin};
 use crate::pac;
-use crate::ppi::{AnyChannel, Event, OneToOneChannel, OneToTwoChannel, Ppi, Task};
+use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};
 use crate::timer::Instance as TimerInstance;
 use crate::timer::{Frequency, Timer};
 use crate::uarte::{Config, Instance as UarteInstance};
@@ -45,8 +45,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> State<'d, U, T> {
 struct StateInner<'d, U: UarteInstance, T: TimerInstance> {
     phantom: PhantomData<&'d mut U>,
     timer: Timer<'d, T>,
-    _ppi_ch1: Ppi<'d, AnyChannel, 1, 2>,
-    _ppi_ch2: Ppi<'d, AnyChannel, 1, 1>,
+    _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>,
+    _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>,
 
     rx: RingBuffer<'d>,
     rx_state: RxState,
@@ -70,8 +70,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
         state: &'d mut State<'d, U, T>,
         _uarte: impl Unborrow<Target = U> + 'd,
         timer: impl Unborrow<Target = T> + 'd,
-        ppi_ch1: impl Unborrow<Target = impl OneToTwoChannel + 'd> + 'd,
-        ppi_ch2: impl Unborrow<Target = impl OneToOneChannel + 'd> + 'd,
+        ppi_ch1: impl Unborrow<Target = impl ConfigurableChannel + 'd> + 'd,
+        ppi_ch2: impl Unborrow<Target = impl ConfigurableChannel + 'd> + 'd,
         irq: impl Unborrow<Target = U::Interrupt> + 'd,
         rxd: impl Unborrow<Target = impl GpioPin> + 'd,
         txd: impl Unborrow<Target = impl GpioPin> + 'd,
@@ -145,20 +145,18 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
         timer.cc(0).short_compare_stop();
 
         let mut ppi_ch1 = Ppi::new_one_to_two(
-            ppi_ch1,
+            ppi_ch1.degrade(),
             Event::from_reg(&r.events_rxdrdy),
             timer.task_clear(),
             timer.task_start(),
-        )
-        .degrade();
+        );
         ppi_ch1.enable();
 
         let mut ppi_ch2 = Ppi::new_one_to_one(
-            ppi_ch2,
+            ppi_ch2.degrade(),
             timer.cc(0).event_compare(),
             Task::from_reg(&r.tasks_stoprx),
-        )
-        .degrade();
+        );
         ppi_ch2.enable();
 
         Self {
diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs
index 326f4a8b..68989648 100644
--- a/embassy-nrf/src/chips/nrf52805.rs
+++ b/embassy-nrf/src/chips/nrf52805.rs
@@ -167,28 +167,28 @@ impl_pin!(P0_29, 0, 29);
 impl_pin!(P0_30, 0, 30);
 impl_pin!(P0_31, 0, 31);
 
-impl_ppi_channel!(PPI_CH0, 0, true, 1, 2);
-impl_ppi_channel!(PPI_CH1, 1, true, 1, 2);
-impl_ppi_channel!(PPI_CH2, 2, true, 1, 2);
-impl_ppi_channel!(PPI_CH3, 3, true, 1, 2);
-impl_ppi_channel!(PPI_CH4, 4, true, 1, 2);
-impl_ppi_channel!(PPI_CH5, 5, true, 1, 2);
-impl_ppi_channel!(PPI_CH6, 6, true, 1, 2);
-impl_ppi_channel!(PPI_CH7, 7, true, 1, 2);
-impl_ppi_channel!(PPI_CH8, 8, true, 1, 2);
-impl_ppi_channel!(PPI_CH9, 9, true, 1, 2);
-impl_ppi_channel!(PPI_CH20, 20, false, 0, 1);
-impl_ppi_channel!(PPI_CH21, 21, false, 0, 1);
-impl_ppi_channel!(PPI_CH22, 22, false, 0, 1);
-impl_ppi_channel!(PPI_CH23, 23, false, 0, 1);
-impl_ppi_channel!(PPI_CH24, 24, false, 0, 1);
-impl_ppi_channel!(PPI_CH25, 25, false, 0, 1);
-impl_ppi_channel!(PPI_CH26, 26, false, 0, 1);
-impl_ppi_channel!(PPI_CH27, 27, false, 0, 1);
-impl_ppi_channel!(PPI_CH28, 28, false, 0, 1);
-impl_ppi_channel!(PPI_CH29, 29, false, 0, 1);
-impl_ppi_channel!(PPI_CH30, 30, false, 0, 1);
-impl_ppi_channel!(PPI_CH31, 31, false, 0, 1);
+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_saadc_input!(P0_04, ANALOGINPUT2);
 impl_saadc_input!(P0_05, ANALOGINPUT3);
diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs
index 4c16d51a..b3b3593b 100644
--- a/embassy-nrf/src/chips/nrf52810.rs
+++ b/embassy-nrf/src/chips/nrf52810.rs
@@ -172,38 +172,38 @@ impl_pin!(P0_29, 0, 29);
 impl_pin!(P0_30, 0, 30);
 impl_pin!(P0_31, 0, 31);
 
-impl_ppi_channel!(PPI_CH0, 0, true, 1, 2);
-impl_ppi_channel!(PPI_CH1, 1, true, 1, 2);
-impl_ppi_channel!(PPI_CH2, 2, true, 1, 2);
-impl_ppi_channel!(PPI_CH3, 3, true, 1, 2);
-impl_ppi_channel!(PPI_CH4, 4, true, 1, 2);
-impl_ppi_channel!(PPI_CH5, 5, true, 1, 2);
-impl_ppi_channel!(PPI_CH6, 6, true, 1, 2);
-impl_ppi_channel!(PPI_CH7, 7, true, 1, 2);
-impl_ppi_channel!(PPI_CH8, 8, true, 1, 2);
-impl_ppi_channel!(PPI_CH9, 9, true, 1, 2);
-impl_ppi_channel!(PPI_CH10, 10, true, 1, 2);
-impl_ppi_channel!(PPI_CH11, 11, true, 1, 2);
-impl_ppi_channel!(PPI_CH12, 12, true, 1, 2);
-impl_ppi_channel!(PPI_CH13, 13, true, 1, 2);
-impl_ppi_channel!(PPI_CH14, 14, true, 1, 2);
-impl_ppi_channel!(PPI_CH15, 15, true, 1, 2);
-impl_ppi_channel!(PPI_CH16, 16, true, 1, 2);
-impl_ppi_channel!(PPI_CH17, 17, true, 1, 2);
-impl_ppi_channel!(PPI_CH18, 18, true, 1, 2);
-impl_ppi_channel!(PPI_CH19, 19, true, 1, 2);
-impl_ppi_channel!(PPI_CH20, 20, false, 0, 1);
-impl_ppi_channel!(PPI_CH21, 21, false, 0, 1);
-impl_ppi_channel!(PPI_CH22, 22, false, 0, 1);
-impl_ppi_channel!(PPI_CH23, 23, false, 0, 1);
-impl_ppi_channel!(PPI_CH24, 24, false, 0, 1);
-impl_ppi_channel!(PPI_CH25, 25, false, 0, 1);
-impl_ppi_channel!(PPI_CH26, 26, false, 0, 1);
-impl_ppi_channel!(PPI_CH27, 27, false, 0, 1);
-impl_ppi_channel!(PPI_CH28, 28, false, 0, 1);
-impl_ppi_channel!(PPI_CH29, 29, false, 0, 1);
-impl_ppi_channel!(PPI_CH30, 30, false, 0, 1);
-impl_ppi_channel!(PPI_CH31, 31, false, 0, 1);
+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_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs
index 0c54d9b9..7551492c 100644
--- a/embassy-nrf/src/chips/nrf52811.rs
+++ b/embassy-nrf/src/chips/nrf52811.rs
@@ -173,38 +173,38 @@ impl_pin!(P0_29, 0, 29);
 impl_pin!(P0_30, 0, 30);
 impl_pin!(P0_31, 0, 31);
 
-impl_ppi_channel!(PPI_CH0, 0, true, 1, 2);
-impl_ppi_channel!(PPI_CH1, 1, true, 1, 2);
-impl_ppi_channel!(PPI_CH2, 2, true, 1, 2);
-impl_ppi_channel!(PPI_CH3, 3, true, 1, 2);
-impl_ppi_channel!(PPI_CH4, 4, true, 1, 2);
-impl_ppi_channel!(PPI_CH5, 5, true, 1, 2);
-impl_ppi_channel!(PPI_CH6, 6, true, 1, 2);
-impl_ppi_channel!(PPI_CH7, 7, true, 1, 2);
-impl_ppi_channel!(PPI_CH8, 8, true, 1, 2);
-impl_ppi_channel!(PPI_CH9, 9, true, 1, 2);
-impl_ppi_channel!(PPI_CH10, 10, true, 1, 2);
-impl_ppi_channel!(PPI_CH11, 11, true, 1, 2);
-impl_ppi_channel!(PPI_CH12, 12, true, 1, 2);
-impl_ppi_channel!(PPI_CH13, 13, true, 1, 2);
-impl_ppi_channel!(PPI_CH14, 14, true, 1, 2);
-impl_ppi_channel!(PPI_CH15, 15, true, 1, 2);
-impl_ppi_channel!(PPI_CH16, 16, true, 1, 2);
-impl_ppi_channel!(PPI_CH17, 17, true, 1, 2);
-impl_ppi_channel!(PPI_CH18, 18, true, 1, 2);
-impl_ppi_channel!(PPI_CH19, 19, true, 1, 2);
-impl_ppi_channel!(PPI_CH20, 20, false, 0, 1);
-impl_ppi_channel!(PPI_CH21, 21, false, 0, 1);
-impl_ppi_channel!(PPI_CH22, 22, false, 0, 1);
-impl_ppi_channel!(PPI_CH23, 23, false, 0, 1);
-impl_ppi_channel!(PPI_CH24, 24, false, 0, 1);
-impl_ppi_channel!(PPI_CH25, 25, false, 0, 1);
-impl_ppi_channel!(PPI_CH26, 26, false, 0, 1);
-impl_ppi_channel!(PPI_CH27, 27, false, 0, 1);
-impl_ppi_channel!(PPI_CH28, 28, false, 0, 1);
-impl_ppi_channel!(PPI_CH29, 29, false, 0, 1);
-impl_ppi_channel!(PPI_CH30, 30, false, 0, 1);
-impl_ppi_channel!(PPI_CH31, 31, false, 0, 1);
+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_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs
index d52f2f98..128e1503 100644
--- a/embassy-nrf/src/chips/nrf52820.rs
+++ b/embassy-nrf/src/chips/nrf52820.rs
@@ -168,38 +168,38 @@ impl_pin!(P0_29, 0, 29);
 impl_pin!(P0_30, 0, 30);
 impl_pin!(P0_31, 0, 31);
 
-impl_ppi_channel!(PPI_CH0, 0, true, 1, 2);
-impl_ppi_channel!(PPI_CH1, 1, true, 1, 2);
-impl_ppi_channel!(PPI_CH2, 2, true, 1, 2);
-impl_ppi_channel!(PPI_CH3, 3, true, 1, 2);
-impl_ppi_channel!(PPI_CH4, 4, true, 1, 2);
-impl_ppi_channel!(PPI_CH5, 5, true, 1, 2);
-impl_ppi_channel!(PPI_CH6, 6, true, 1, 2);
-impl_ppi_channel!(PPI_CH7, 7, true, 1, 2);
-impl_ppi_channel!(PPI_CH8, 8, true, 1, 2);
-impl_ppi_channel!(PPI_CH9, 9, true, 1, 2);
-impl_ppi_channel!(PPI_CH10, 10, true, 1, 2);
-impl_ppi_channel!(PPI_CH11, 11, true, 1, 2);
-impl_ppi_channel!(PPI_CH12, 12, true, 1, 2);
-impl_ppi_channel!(PPI_CH13, 13, true, 1, 2);
-impl_ppi_channel!(PPI_CH14, 14, true, 1, 2);
-impl_ppi_channel!(PPI_CH15, 15, true, 1, 2);
-impl_ppi_channel!(PPI_CH16, 16, true, 1, 2);
-impl_ppi_channel!(PPI_CH17, 17, true, 1, 2);
-impl_ppi_channel!(PPI_CH18, 18, true, 1, 2);
-impl_ppi_channel!(PPI_CH19, 19, true, 1, 2);
-impl_ppi_channel!(PPI_CH20, 20, false, 0, 1);
-impl_ppi_channel!(PPI_CH21, 21, false, 0, 1);
-impl_ppi_channel!(PPI_CH22, 22, false, 0, 1);
-impl_ppi_channel!(PPI_CH23, 23, false, 0, 1);
-impl_ppi_channel!(PPI_CH24, 24, false, 0, 1);
-impl_ppi_channel!(PPI_CH25, 25, false, 0, 1);
-impl_ppi_channel!(PPI_CH26, 26, false, 0, 1);
-impl_ppi_channel!(PPI_CH27, 27, false, 0, 1);
-impl_ppi_channel!(PPI_CH28, 28, false, 0, 1);
-impl_ppi_channel!(PPI_CH29, 29, false, 0, 1);
-impl_ppi_channel!(PPI_CH30, 30, false, 0, 1);
-impl_ppi_channel!(PPI_CH31, 31, false, 0, 1);
+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);
 
 pub mod irqs {
     use crate::pac::Interrupt as InterruptEnum;
diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs
index d97a6535..432af143 100644
--- a/embassy-nrf/src/chips/nrf52832.rs
+++ b/embassy-nrf/src/chips/nrf52832.rs
@@ -190,38 +190,38 @@ impl_pin!(P0_29, 0, 29);
 impl_pin!(P0_30, 0, 30);
 impl_pin!(P0_31, 0, 31);
 
-impl_ppi_channel!(PPI_CH0, 0, true, 1, 2);
-impl_ppi_channel!(PPI_CH1, 1, true, 1, 2);
-impl_ppi_channel!(PPI_CH2, 2, true, 1, 2);
-impl_ppi_channel!(PPI_CH3, 3, true, 1, 2);
-impl_ppi_channel!(PPI_CH4, 4, true, 1, 2);
-impl_ppi_channel!(PPI_CH5, 5, true, 1, 2);
-impl_ppi_channel!(PPI_CH6, 6, true, 1, 2);
-impl_ppi_channel!(PPI_CH7, 7, true, 1, 2);
-impl_ppi_channel!(PPI_CH8, 8, true, 1, 2);
-impl_ppi_channel!(PPI_CH9, 9, true, 1, 2);
-impl_ppi_channel!(PPI_CH10, 10, true, 1, 2);
-impl_ppi_channel!(PPI_CH11, 11, true, 1, 2);
-impl_ppi_channel!(PPI_CH12, 12, true, 1, 2);
-impl_ppi_channel!(PPI_CH13, 13, true, 1, 2);
-impl_ppi_channel!(PPI_CH14, 14, true, 1, 2);
-impl_ppi_channel!(PPI_CH15, 15, true, 1, 2);
-impl_ppi_channel!(PPI_CH16, 16, true, 1, 2);
-impl_ppi_channel!(PPI_CH17, 17, true, 1, 2);
-impl_ppi_channel!(PPI_CH18, 18, true, 1, 2);
-impl_ppi_channel!(PPI_CH19, 19, true, 1, 2);
-impl_ppi_channel!(PPI_CH20, 20, false, 0, 1);
-impl_ppi_channel!(PPI_CH21, 21, false, 0, 1);
-impl_ppi_channel!(PPI_CH22, 22, false, 0, 1);
-impl_ppi_channel!(PPI_CH23, 23, false, 0, 1);
-impl_ppi_channel!(PPI_CH24, 24, false, 0, 1);
-impl_ppi_channel!(PPI_CH25, 25, false, 0, 1);
-impl_ppi_channel!(PPI_CH26, 26, false, 0, 1);
-impl_ppi_channel!(PPI_CH27, 27, false, 0, 1);
-impl_ppi_channel!(PPI_CH28, 28, false, 0, 1);
-impl_ppi_channel!(PPI_CH29, 29, false, 0, 1);
-impl_ppi_channel!(PPI_CH30, 30, false, 0, 1);
-impl_ppi_channel!(PPI_CH31, 31, false, 0, 1);
+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_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs
index 9bf370c4..7c7198df 100644
--- a/embassy-nrf/src/chips/nrf52833.rs
+++ b/embassy-nrf/src/chips/nrf52833.rs
@@ -226,38 +226,38 @@ impl_pin!(P1_13, 1, 13);
 impl_pin!(P1_14, 1, 14);
 impl_pin!(P1_15, 1, 15);
 
-impl_ppi_channel!(PPI_CH0, 0, true, 1, 2);
-impl_ppi_channel!(PPI_CH1, 1, true, 1, 2);
-impl_ppi_channel!(PPI_CH2, 2, true, 1, 2);
-impl_ppi_channel!(PPI_CH3, 3, true, 1, 2);
-impl_ppi_channel!(PPI_CH4, 4, true, 1, 2);
-impl_ppi_channel!(PPI_CH5, 5, true, 1, 2);
-impl_ppi_channel!(PPI_CH6, 6, true, 1, 2);
-impl_ppi_channel!(PPI_CH7, 7, true, 1, 2);
-impl_ppi_channel!(PPI_CH8, 8, true, 1, 2);
-impl_ppi_channel!(PPI_CH9, 9, true, 1, 2);
-impl_ppi_channel!(PPI_CH10, 10, true, 1, 2);
-impl_ppi_channel!(PPI_CH11, 11, true, 1, 2);
-impl_ppi_channel!(PPI_CH12, 12, true, 1, 2);
-impl_ppi_channel!(PPI_CH13, 13, true, 1, 2);
-impl_ppi_channel!(PPI_CH14, 14, true, 1, 2);
-impl_ppi_channel!(PPI_CH15, 15, true, 1, 2);
-impl_ppi_channel!(PPI_CH16, 16, true, 1, 2);
-impl_ppi_channel!(PPI_CH17, 17, true, 1, 2);
-impl_ppi_channel!(PPI_CH18, 18, true, 1, 2);
-impl_ppi_channel!(PPI_CH19, 19, true, 1, 2);
-impl_ppi_channel!(PPI_CH20, 20, false, 0, 1);
-impl_ppi_channel!(PPI_CH21, 21, false, 0, 1);
-impl_ppi_channel!(PPI_CH22, 22, false, 0, 1);
-impl_ppi_channel!(PPI_CH23, 23, false, 0, 1);
-impl_ppi_channel!(PPI_CH24, 24, false, 0, 1);
-impl_ppi_channel!(PPI_CH25, 25, false, 0, 1);
-impl_ppi_channel!(PPI_CH26, 26, false, 0, 1);
-impl_ppi_channel!(PPI_CH27, 27, false, 0, 1);
-impl_ppi_channel!(PPI_CH28, 28, false, 0, 1);
-impl_ppi_channel!(PPI_CH29, 29, false, 0, 1);
-impl_ppi_channel!(PPI_CH30, 30, false, 0, 1);
-impl_ppi_channel!(PPI_CH31, 31, false, 0, 1);
+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_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs
index 266328d5..f5b90cd5 100644
--- a/embassy-nrf/src/chips/nrf52840.rs
+++ b/embassy-nrf/src/chips/nrf52840.rs
@@ -231,38 +231,38 @@ impl_pin!(P1_13, 1, 13);
 impl_pin!(P1_14, 1, 14);
 impl_pin!(P1_15, 1, 15);
 
-impl_ppi_channel!(PPI_CH0, 0, true, 1, 2);
-impl_ppi_channel!(PPI_CH1, 1, true, 1, 2);
-impl_ppi_channel!(PPI_CH2, 2, true, 1, 2);
-impl_ppi_channel!(PPI_CH3, 3, true, 1, 2);
-impl_ppi_channel!(PPI_CH4, 4, true, 1, 2);
-impl_ppi_channel!(PPI_CH5, 5, true, 1, 2);
-impl_ppi_channel!(PPI_CH6, 6, true, 1, 2);
-impl_ppi_channel!(PPI_CH7, 7, true, 1, 2);
-impl_ppi_channel!(PPI_CH8, 8, true, 1, 2);
-impl_ppi_channel!(PPI_CH9, 9, true, 1, 2);
-impl_ppi_channel!(PPI_CH10, 10, true, 1, 2);
-impl_ppi_channel!(PPI_CH11, 11, true, 1, 2);
-impl_ppi_channel!(PPI_CH12, 12, true, 1, 2);
-impl_ppi_channel!(PPI_CH13, 13, true, 1, 2);
-impl_ppi_channel!(PPI_CH14, 14, true, 1, 2);
-impl_ppi_channel!(PPI_CH15, 15, true, 1, 2);
-impl_ppi_channel!(PPI_CH16, 16, true, 1, 2);
-impl_ppi_channel!(PPI_CH17, 17, true, 1, 2);
-impl_ppi_channel!(PPI_CH18, 18, true, 1, 2);
-impl_ppi_channel!(PPI_CH19, 19, true, 1, 2);
-impl_ppi_channel!(PPI_CH20, 20, false, 0, 1);
-impl_ppi_channel!(PPI_CH21, 21, false, 0, 1);
-impl_ppi_channel!(PPI_CH22, 22, false, 0, 1);
-impl_ppi_channel!(PPI_CH23, 23, false, 0, 1);
-impl_ppi_channel!(PPI_CH24, 24, false, 0, 1);
-impl_ppi_channel!(PPI_CH25, 25, false, 0, 1);
-impl_ppi_channel!(PPI_CH26, 26, false, 0, 1);
-impl_ppi_channel!(PPI_CH27, 27, false, 0, 1);
-impl_ppi_channel!(PPI_CH28, 28, false, 0, 1);
-impl_ppi_channel!(PPI_CH29, 29, false, 0, 1);
-impl_ppi_channel!(PPI_CH30, 30, false, 0, 1);
-impl_ppi_channel!(PPI_CH31, 31, false, 0, 1);
+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_saadc_input!(P0_02, ANALOGINPUT0);
 impl_saadc_input!(P0_03, ANALOGINPUT1);
diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs
index 7357e9e2..6ca91810 100644
--- a/embassy-nrf/src/chips/nrf9160.rs
+++ b/embassy-nrf/src/chips/nrf9160.rs
@@ -184,22 +184,22 @@ impl_pin!(P0_29, 0, 29);
 impl_pin!(P0_30, 0, 30);
 impl_pin!(P0_31, 0, 31);
 
-impl_ppi_channel!(PPI_CH0, 0, true, many, many);
-impl_ppi_channel!(PPI_CH1, 1, true, many, many);
-impl_ppi_channel!(PPI_CH2, 2, true, many, many);
-impl_ppi_channel!(PPI_CH3, 3, true, many, many);
-impl_ppi_channel!(PPI_CH4, 4, true, many, many);
-impl_ppi_channel!(PPI_CH5, 5, true, many, many);
-impl_ppi_channel!(PPI_CH6, 6, true, many, many);
-impl_ppi_channel!(PPI_CH7, 7, true, many, many);
-impl_ppi_channel!(PPI_CH8, 8, true, many, many);
-impl_ppi_channel!(PPI_CH9, 9, true, many, many);
-impl_ppi_channel!(PPI_CH10, 10, true, many, many);
-impl_ppi_channel!(PPI_CH11, 11, true, many, many);
-impl_ppi_channel!(PPI_CH12, 12, true, many, many);
-impl_ppi_channel!(PPI_CH13, 13, true, many, many);
-impl_ppi_channel!(PPI_CH14, 14, true, many, many);
-impl_ppi_channel!(PPI_CH15, 15, true, many, many);
+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_saadc_input!(P0_13, ANALOGINPUT0);
 impl_saadc_input!(P0_14, ANALOGINPUT1);
diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs
index bfbc69b7..cea8a0e6 100644
--- a/embassy-nrf/src/ppi/mod.rs
+++ b/embassy-nrf/src/ppi/mod.rs
@@ -38,21 +38,6 @@ pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize
 impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
     Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
 {
-    pub fn degrade(self) -> Ppi<'d, AnyChannel, EVENT_COUNT, TASK_COUNT> {
-        Ppi {
-            ch: AnyChannel {
-                number: self.ch.number() as u8,
-                #[cfg(feature = "_ppi")]
-                has_configurable_task: self.ch.is_task_configurable(),
-            },
-            #[cfg(feature = "_dppi")]
-            events: self.events,
-            #[cfg(feature = "_dppi")]
-            tasks: self.tasks,
-            phantom: PhantomData,
-        }
-    }
-
     /// Enables the channel.
     pub fn enable(&mut self) {
         let r = unsafe { &*pac::PPI::ptr() };
@@ -77,7 +62,8 @@ impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop
     }
 }
 
-impl<'d, C: ZeroToOneChannel> Ppi<'d, C, 0, 1> {
+#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
+impl<'d, C: StaticChannel> Ppi<'d, C, 0, 1> {
     pub fn new_zero_to_one(ch: impl Unborrow<Target = C> + 'd, task: Task) -> Self {
         unborrow!(ch);
 
@@ -97,7 +83,7 @@ impl<'d, C: ZeroToOneChannel> Ppi<'d, C, 0, 1> {
     }
 }
 
-impl<'d, C: OneToOneChannel> Ppi<'d, C, 1, 1> {
+impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
     pub fn new_one_to_one(ch: impl Unborrow<Target = C> + 'd, event: Event, task: Task) -> Self {
         unborrow!(ch);
 
@@ -117,7 +103,8 @@ impl<'d, C: OneToOneChannel> Ppi<'d, C, 1, 1> {
     }
 }
 
-impl<'d, C: OneToTwoChannel> Ppi<'d, C, 1, 2> {
+#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
+impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
     pub fn new_one_to_two(
         ch: impl Unborrow<Target = C> + 'd,
         event: Event,
@@ -142,7 +129,8 @@ impl<'d, C: OneToTwoChannel> Ppi<'d, C, 1, 2> {
     }
 }
 
-impl<'d, C: ManyToManyChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
+#[cfg(feature = "_dppi")]
+impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
     Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
 {
     pub fn new_many_to_many(
@@ -221,72 +209,107 @@ pub(crate) mod sealed {
 pub trait Channel: sealed::Channel + Unborrow<Target = Self> + Sized {
     /// Returns the number of the channel
     fn number(&self) -> usize;
-    #[cfg(feature = "_ppi")]
-    fn is_task_configurable(&self) -> bool;
+    fn configurable() -> bool;
 }
 
-pub trait ZeroToOneChannel: Channel {}
-pub trait OneToOneChannel: ZeroToOneChannel {}
-pub trait OneToTwoChannel: OneToOneChannel {}
-pub trait ManyToManyChannel: OneToTwoChannel {}
+pub trait ConfigurableChannel: Channel {
+    fn degrade(self) -> AnyConfigurableChannel;
+}
+
+pub trait StaticChannel: Channel {
+    fn degrade(self) -> AnyStaticChannel;
+}
 
 pub trait Group: sealed::Group + Sized {
     fn number(&self) -> usize;
+    fn degrade(self) -> AnyGroup {
+        AnyGroup {
+            number: self.number() as u8,
+        }
+    }
 }
 
 // ======================
 //       channels
 
-pub struct AnyChannel {
-    number: u8,
-    #[cfg(feature = "_ppi")]
-    has_configurable_task: bool,
+/// The any channel can represent any static channel at runtime.
+/// This can be used to have fewer generic parameters in some places.
+pub struct AnyStaticChannel {
+    pub(crate) number: u8,
 }
-unsafe_impl_unborrow!(AnyChannel);
-impl sealed::Channel for AnyChannel {}
-impl Channel for AnyChannel {
+unsafe_impl_unborrow!(AnyStaticChannel);
+impl sealed::Channel for AnyStaticChannel {}
+impl Channel for AnyStaticChannel {
     fn number(&self) -> usize {
         self.number as usize
     }
 
-    #[cfg(feature = "_ppi")]
-    fn is_task_configurable(&self) -> bool {
-        self.has_configurable_task
+    fn configurable() -> bool {
+        false
+    }
+}
+impl StaticChannel for AnyStaticChannel {
+    fn degrade(self) -> AnyStaticChannel {
+        self
+    }
+}
+
+/// The any configurable channel can represent any configurable channel at runtime.
+/// This can be used to have fewer generic parameters in some places.
+pub struct AnyConfigurableChannel {
+    pub(crate) number: u8,
+}
+unsafe_impl_unborrow!(AnyConfigurableChannel);
+impl sealed::Channel for AnyConfigurableChannel {}
+impl Channel for AnyConfigurableChannel {
+    fn number(&self) -> usize {
+        self.number as usize
+    }
+
+    fn configurable() -> bool {
+        true
+    }
+}
+impl ConfigurableChannel for AnyConfigurableChannel {
+    fn degrade(self) -> AnyConfigurableChannel {
+        self
     }
 }
 
 macro_rules! impl_ppi_channel {
-    ($type:ident, $number:expr, $has_configurable_task:expr) => {
+    ($type:ident, $number:expr, $configurability:expr) => {
         impl crate::ppi::sealed::Channel for peripherals::$type {}
         impl crate::ppi::Channel for peripherals::$type {
             fn number(&self) -> usize {
                 $number
             }
 
-            #[cfg(feature = "_ppi")]
-            fn is_task_configurable(&self) -> bool {
-                $has_configurable_task
+            fn configurable() -> bool {
+                $configurability
             }
         }
     };
-    ($type:ident, $number:expr, $has_configurable_task:expr, 0, 0) => {
-        impl_ppi_channel!($type, $number, $has_configurable_task);
+    ($type:ident, $number:expr => static) => {
+        impl_ppi_channel!($type, $number, false);
+        impl crate::ppi::StaticChannel for peripherals::$type {
+            fn degrade(self) -> crate::ppi::AnyStaticChannel {
+                use crate::ppi::Channel;
+                crate::ppi::AnyStaticChannel {
+                    number: self.number() as u8,
+                }
+            }
+        }
     };
-    ($type:ident, $number:expr, $has_configurable_task:expr, 0, 1) => {
-        impl_ppi_channel!($type, $number, $has_configurable_task, 0, 0);
-        impl crate::ppi::ZeroToOneChannel for peripherals::$type {}
-    };
-    ($type:ident, $number:expr, $has_configurable_task:expr, 1, 1) => {
-        impl_ppi_channel!($type, $number, $has_configurable_task, 0, 1);
-        impl crate::ppi::OneToOneChannel for peripherals::$type {}
-    };
-    ($type:ident, $number:expr, $has_configurable_task:expr, 1, 2) => {
-        impl_ppi_channel!($type, $number, $has_configurable_task, 1, 1);
-        impl crate::ppi::OneToTwoChannel for peripherals::$type {}
-    };
-    ($type:ident, $number:expr, $has_configurable_task:expr, many, many) => {
-        impl_ppi_channel!($type, $number, $has_configurable_task, 1, 2);
-        impl crate::ppi::ManyToManyChannel for peripherals::$type {}
+    ($type:ident, $number:expr => configurable) => {
+        impl_ppi_channel!($type, $number, true);
+        impl crate::ppi::ConfigurableChannel for peripherals::$type {
+            fn degrade(self) -> crate::ppi::AnyConfigurableChannel {
+                use crate::ppi::Channel;
+                crate::ppi::AnyConfigurableChannel {
+                    number: self.number() as u8,
+                }
+            }
+        }
     };
 }
 
diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs
index 67d2086c..0bcb3a79 100644
--- a/embassy-nrf/src/ppi/ppi.rs
+++ b/embassy-nrf/src/ppi/ppi.rs
@@ -15,6 +15,7 @@ impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
         }
     }
 
+    #[cfg(not(feature = "nrf51"))]
     fn set_fork_task(task: Option<&Task>, channel: usize) {
         let r = unsafe { &*pac::PPI::ptr() };
         if let Some(task) = task {
@@ -40,16 +41,18 @@ impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
     /// Enables all tasks and events
     pub(super) fn enable_all(tasks: &[Task], events: &[Event], channel: &C) {
         // One configurable task, no fork
-        if channel.is_task_configurable() && TASK_COUNT == 1 {
+        if C::configurable() && TASK_COUNT == 1 {
             Self::set_main_task(Some(&tasks[0]), channel.number());
         }
 
         // One configurable task, as fork
-        if !channel.is_task_configurable() && TASK_COUNT == 1 {
+        #[cfg(not(feature = "nrf51"))]
+        if !C::configurable() && TASK_COUNT == 1 {
             Self::set_fork_task(Some(&tasks[0]), channel.number());
         }
 
         // Two configurable tasks (main + fork)
+        #[cfg(not(feature = "nrf51"))]
         if TASK_COUNT == 2 {
             Self::set_main_task(Some(&tasks[0]), channel.number());
             Self::set_fork_task(Some(&tasks[1]), channel.number());
@@ -62,11 +65,12 @@ impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
 
     /// Disable all tasks and events
     pub(super) fn disable_all(&self) {
-        if self.ch.is_task_configurable() {
+        if C::configurable() {
             Self::set_main_task(None, self.ch.number());
         }
 
-        if TASK_COUNT == 1 && !self.ch.is_task_configurable() || TASK_COUNT == 2 {
+        #[cfg(not(feature = "nrf51"))]
+        if TASK_COUNT == 1 && !C::configurable() || TASK_COUNT == 2 {
             Self::set_fork_task(None, self.ch.number());
         }
 
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs
index 36cf65d8..4bbdec63 100644
--- a/embassy-nrf/src/uarte.rs
+++ b/embassy-nrf/src/uarte.rs
@@ -18,7 +18,7 @@ use crate::gpio::sealed::Pin as _;
 use crate::gpio::{self, OptionalPin as GpioOptionalPin, Pin as GpioPin};
 use crate::interrupt::Interrupt;
 use crate::pac;
-use crate::ppi::{AnyChannel, Event, OneToOneChannel, OneToTwoChannel, Ppi, Task};
+use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};
 use crate::timer::Instance as TimerInstance;
 use crate::timer::{Frequency, Timer};
 
@@ -331,8 +331,8 @@ impl<'d, T: Instance> Write for Uarte<'d, T> {
 pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> {
     uarte: Uarte<'d, U>,
     timer: Timer<'d, T>,
-    ppi_ch1: Ppi<'d, AnyChannel, 1, 2>,
-    _ppi_ch2: Ppi<'d, AnyChannel, 1, 1>,
+    ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>,
+    _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>,
 }
 
 impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
@@ -348,8 +348,8 @@ impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
     pub unsafe fn new(
         uarte: impl Unborrow<Target = U> + 'd,
         timer: impl Unborrow<Target = T> + 'd,
-        ppi_ch1: impl Unborrow<Target = impl OneToTwoChannel + 'd> + 'd,
-        ppi_ch2: impl Unborrow<Target = impl OneToOneChannel + 'd> + 'd,
+        ppi_ch1: impl Unborrow<Target = impl ConfigurableChannel + 'd> + 'd,
+        ppi_ch2: impl Unborrow<Target = impl ConfigurableChannel + 'd> + 'd,
         irq: impl Unborrow<Target = U::Interrupt> + 'd,
         rxd: impl Unborrow<Target = impl GpioPin> + 'd,
         txd: impl Unborrow<Target = impl GpioPin> + 'd,
@@ -379,20 +379,18 @@ impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
         timer.cc(0).short_compare_stop();
 
         let mut ppi_ch1 = Ppi::new_one_to_two(
-            ppi_ch1,
+            ppi_ch1.degrade(),
             Event::from_reg(&r.events_rxdrdy),
             timer.task_clear(),
             timer.task_start(),
-        )
-        .degrade();
+        );
         ppi_ch1.enable();
 
         let mut ppi_ch2 = Ppi::new_one_to_one(
-            ppi_ch2,
+            ppi_ch2.degrade(),
             timer.cc(0).event_compare(),
             Task::from_reg(&r.tasks_stoprx),
-        )
-        .degrade();
+        );
         ppi_ch2.enable();
 
         Self {

From 36d3eda2f9d24be5a581e95badd74bce7060572e Mon Sep 17 00:00:00 2001
From: Dario Nieuwenhuis <dirbaio@dirbaio.net>
Date: Tue, 26 Oct 2021 16:10:34 +0200
Subject: [PATCH 10/10] ppi: simplify driver creation.

Moving `new_*` to the version-specific mod allows doing the correct
register writes right there in `new`, without needing abstractions
like `enable_all`/`disable_all`.
---
 embassy-nrf/src/ppi/dppi.rs | 105 +++++++++++++-----------
 embassy-nrf/src/ppi/mod.rs  | 121 +---------------------------
 embassy-nrf/src/ppi/ppi.rs  | 154 +++++++++++++++++++-----------------
 3 files changed, 143 insertions(+), 237 deletions(-)

diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs
index 083ec858..b3676fca 100644
--- a/embassy-nrf/src/ppi/dppi.rs
+++ b/embassy-nrf/src/ppi/dppi.rs
@@ -1,61 +1,74 @@
-use super::{Channel, Event, Ppi, Task};
+use core::marker::PhantomData;
+
+use embassy::util::Unborrow;
+use embassy_hal_common::unborrow;
+
+use super::{Channel, ConfigurableChannel, Event, Ppi, Task};
 
 const DPPI_ENABLE_BIT: u32 = 0x8000_0000;
 const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF;
 
-impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
+impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
+    pub fn new_one_to_one(ch: impl Unborrow<Target = C> + 'd, event: Event, task: Task) -> Self {
+        Ppi::new_many_to_many(ch, [event], [task])
+    }
+}
+
+impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
+    pub fn new_one_to_two(
+        ch: impl Unborrow<Target = C> + 'd,
+        event: Event,
+        task1: Task,
+        task2: Task,
+    ) -> Self {
+        Ppi::new_many_to_many(ch, [event], [task1, task2])
+    }
+}
+
+impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
     Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
 {
-    pub(super) fn enable_task(task: &Task, channel: &C, _index: usize) {
-        unsafe {
-            if task.subscribe_reg().read_volatile() != 0 {
+    pub fn new_many_to_many(
+        ch: impl Unborrow<Target = C> + 'd,
+        events: [Event; EVENT_COUNT],
+        tasks: [Task; TASK_COUNT],
+    ) -> Self {
+        unborrow!(ch);
+
+        let val = DPPI_ENABLE_BIT | (ch.number() as u32 & DPPI_CHANNEL_MASK);
+        for task in tasks {
+            if unsafe { task.subscribe_reg().read_volatile() } != 0 {
                 panic!("Task is already in use");
             }
-            task.subscribe_reg()
-                .write_volatile(DPPI_ENABLE_BIT | (channel.number() as u32 & DPPI_CHANNEL_MASK));
+            unsafe { task.subscribe_reg().write_volatile(val) }
         }
-    }
-
-    pub(super) fn disable_task(task: &Task, _channel: &C, _index: usize) {
-        unsafe {
-            task.subscribe_reg().write_volatile(0);
-        }
-    }
-
-    pub(super) fn enable_event(event: &Event, channel: &C, _index: usize) {
-        unsafe {
-            if event.publish_reg().read_volatile() != 0 {
-                panic!("Task is already in use");
+        for event in events {
+            if unsafe { event.publish_reg().read_volatile() } != 0 {
+                panic!("Event is already in use");
             }
-            event
-                .publish_reg()
-                .write_volatile(DPPI_ENABLE_BIT | (channel.number() as u32 & DPPI_CHANNEL_MASK));
+            unsafe { event.publish_reg().write_volatile(val) }
         }
-    }
 
-    pub(super) fn disable_event(event: &Event, _channel: &C, _index: usize) {
-        unsafe {
-            event.publish_reg().write_volatile(0);
-        }
-    }
-
-    /// Enables all tasks and events
-    pub(super) fn enable_all(tasks: &[Task], events: &[Event], channel: &C) {
-        for (index, task) in tasks.iter().enumerate() {
-            Self::enable_task(task, channel, index);
-        }
-        for (index, event) in events.iter().enumerate() {
-            Self::enable_event(event, channel, index);
-        }
-    }
-
-    /// Disable all tasks and events
-    pub(super) fn disable_all(&self) {
-        for (index, task) in self.tasks.iter().enumerate() {
-            Self::disable_task(task, &self.ch, index);
-        }
-        for (index, event) in self.events.iter().enumerate() {
-            Self::disable_event(event, &self.ch, index);
+        Self {
+            ch,
+            events,
+            tasks,
+            phantom: PhantomData,
+        }
+    }
+}
+
+impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop
+    for Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
+{
+    fn drop(&mut self) {
+        self.disable();
+
+        for task in self.tasks {
+            unsafe { task.subscribe_reg().write_volatile(0) }
+        }
+        for event in self.events {
+            unsafe { event.publish_reg().write_volatile(0) }
         }
     }
 }
diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs
index cea8a0e6..96f867d1 100644
--- a/embassy-nrf/src/ppi/mod.rs
+++ b/embassy-nrf/src/ppi/mod.rs
@@ -19,7 +19,7 @@ use crate::{pac, peripherals};
 use core::marker::PhantomData;
 use core::ptr::NonNull;
 use embassy::util::Unborrow;
-use embassy_hal_common::{unborrow, unsafe_impl_unborrow};
+use embassy_hal_common::unsafe_impl_unborrow;
 
 #[cfg(feature = "_dppi")]
 mod dppi;
@@ -53,106 +53,6 @@ impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
     }
 }
 
-impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop
-    for Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
-{
-    fn drop(&mut self) {
-        self.disable();
-        self.disable_all();
-    }
-}
-
-#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
-impl<'d, C: StaticChannel> Ppi<'d, C, 0, 1> {
-    pub fn new_zero_to_one(ch: impl Unborrow<Target = C> + 'd, task: Task) -> Self {
-        unborrow!(ch);
-
-        let events = [];
-        let tasks = [task];
-
-        Self::enable_all(&tasks, &events, &ch);
-
-        Self {
-            ch,
-            #[cfg(feature = "_dppi")]
-            events,
-            #[cfg(feature = "_dppi")]
-            tasks,
-            phantom: PhantomData,
-        }
-    }
-}
-
-impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
-    pub fn new_one_to_one(ch: impl Unborrow<Target = C> + 'd, event: Event, task: Task) -> Self {
-        unborrow!(ch);
-
-        let events = [event];
-        let tasks = [task];
-
-        Self::enable_all(&tasks, &events, &ch);
-
-        Self {
-            ch,
-            #[cfg(feature = "_dppi")]
-            events,
-            #[cfg(feature = "_dppi")]
-            tasks,
-            phantom: PhantomData,
-        }
-    }
-}
-
-#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
-impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
-    pub fn new_one_to_two(
-        ch: impl Unborrow<Target = C> + 'd,
-        event: Event,
-        task1: Task,
-        task2: Task,
-    ) -> Self {
-        unborrow!(ch);
-
-        let events = [event];
-        let tasks = [task1, task2];
-
-        Self::enable_all(&tasks, &events, &ch);
-
-        Self {
-            ch,
-            #[cfg(feature = "_dppi")]
-            events,
-            #[cfg(feature = "_dppi")]
-            tasks,
-            phantom: PhantomData,
-        }
-    }
-}
-
-#[cfg(feature = "_dppi")]
-impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
-    Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
-{
-    pub fn new_many_to_many(
-        ch: impl Unborrow<Target = C> + 'd,
-        events: [Event; EVENT_COUNT],
-        tasks: [Task; TASK_COUNT],
-    ) -> Self {
-        unborrow!(ch);
-
-        Self::enable_all(&tasks, &events, &ch);
-
-        Self {
-            ch,
-            #[cfg(feature = "_dppi")]
-            events,
-            #[cfg(feature = "_dppi")]
-            tasks,
-            phantom: PhantomData,
-        }
-    }
-}
-
 const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
 
 /// Represents a task that a peripheral can do.
@@ -209,7 +109,6 @@ pub(crate) mod sealed {
 pub trait Channel: sealed::Channel + Unborrow<Target = Self> + Sized {
     /// Returns the number of the channel
     fn number(&self) -> usize;
-    fn configurable() -> bool;
 }
 
 pub trait ConfigurableChannel: Channel {
@@ -243,10 +142,6 @@ impl Channel for AnyStaticChannel {
     fn number(&self) -> usize {
         self.number as usize
     }
-
-    fn configurable() -> bool {
-        false
-    }
 }
 impl StaticChannel for AnyStaticChannel {
     fn degrade(self) -> AnyStaticChannel {
@@ -265,10 +160,6 @@ impl Channel for AnyConfigurableChannel {
     fn number(&self) -> usize {
         self.number as usize
     }
-
-    fn configurable() -> bool {
-        true
-    }
 }
 impl ConfigurableChannel for AnyConfigurableChannel {
     fn degrade(self) -> AnyConfigurableChannel {
@@ -277,20 +168,16 @@ impl ConfigurableChannel for AnyConfigurableChannel {
 }
 
 macro_rules! impl_ppi_channel {
-    ($type:ident, $number:expr, $configurability:expr) => {
+    ($type:ident, $number:expr) => {
         impl crate::ppi::sealed::Channel for peripherals::$type {}
         impl crate::ppi::Channel for peripherals::$type {
             fn number(&self) -> usize {
                 $number
             }
-
-            fn configurable() -> bool {
-                $configurability
-            }
         }
     };
     ($type:ident, $number:expr => static) => {
-        impl_ppi_channel!($type, $number, false);
+        impl_ppi_channel!($type, $number);
         impl crate::ppi::StaticChannel for peripherals::$type {
             fn degrade(self) -> crate::ppi::AnyStaticChannel {
                 use crate::ppi::Channel;
@@ -301,7 +188,7 @@ macro_rules! impl_ppi_channel {
         }
     };
     ($type:ident, $number:expr => configurable) => {
-        impl_ppi_channel!($type, $number, true);
+        impl_ppi_channel!($type, $number);
         impl crate::ppi::ConfigurableChannel for peripherals::$type {
             fn degrade(self) -> crate::ppi::AnyConfigurableChannel {
                 use crate::ppi::Channel;
diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs
index 0bcb3a79..c1d9794c 100644
--- a/embassy-nrf/src/ppi/ppi.rs
+++ b/embassy-nrf/src/ppi/ppi.rs
@@ -1,81 +1,87 @@
-use super::{Channel, Event, Ppi, Task};
+use core::marker::PhantomData;
+
+use embassy::util::Unborrow;
+use embassy_hal_common::unborrow;
+
+use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task};
 use crate::pac;
 
-impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
-    Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
-{
-    fn set_main_task(task: Option<&Task>, channel: usize) {
+impl Task {
+    fn reg_val(&self) -> u32 {
+        self.0.as_ptr() as _
+    }
+}
+impl Event {
+    fn reg_val(&self) -> u32 {
+        self.0.as_ptr() as _
+    }
+}
+
+#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
+impl<'d, C: StaticChannel> Ppi<'d, C, 0, 1> {
+    pub fn new_zero_to_one(ch: impl Unborrow<Target = C> + 'd, task: Task) -> Self {
+        unborrow!(ch);
+
         let r = unsafe { &*pac::PPI::ptr() };
-        if let Some(task) = task {
-            r.ch[channel]
-                .tep
-                .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
-        } else {
-            r.ch[channel].tep.write(|w| unsafe { w.bits(0) })
-        }
-    }
+        let n = ch.number();
+        r.fork[n].tep.write(|w| unsafe { w.bits(task.reg_val()) });
 
-    #[cfg(not(feature = "nrf51"))]
-    fn set_fork_task(task: Option<&Task>, channel: usize) {
-        let r = unsafe { &*pac::PPI::ptr() };
-        if let Some(task) = task {
-            r.fork[channel]
-                .tep
-                .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
-        } else {
-            r.fork[channel].tep.write(|w| unsafe { w.bits(0) })
-        }
-    }
-
-    fn set_event(event: Option<&Event>, channel: usize) {
-        let r = unsafe { &*pac::PPI::ptr() };
-        if let Some(event) = event {
-            r.ch[channel]
-                .eep
-                .write(|w| unsafe { w.bits(event.0.as_ptr() as u32) })
-        } else {
-            r.ch[channel].eep.write(|w| unsafe { w.bits(0) })
-        }
-    }
-
-    /// Enables all tasks and events
-    pub(super) fn enable_all(tasks: &[Task], events: &[Event], channel: &C) {
-        // One configurable task, no fork
-        if C::configurable() && TASK_COUNT == 1 {
-            Self::set_main_task(Some(&tasks[0]), channel.number());
-        }
-
-        // One configurable task, as fork
-        #[cfg(not(feature = "nrf51"))]
-        if !C::configurable() && TASK_COUNT == 1 {
-            Self::set_fork_task(Some(&tasks[0]), channel.number());
-        }
-
-        // Two configurable tasks (main + fork)
-        #[cfg(not(feature = "nrf51"))]
-        if TASK_COUNT == 2 {
-            Self::set_main_task(Some(&tasks[0]), channel.number());
-            Self::set_fork_task(Some(&tasks[1]), channel.number());
-        }
-
-        if EVENT_COUNT == 1 {
-            Self::set_event(Some(&events[0]), channel.number());
-        }
-    }
-
-    /// Disable all tasks and events
-    pub(super) fn disable_all(&self) {
-        if C::configurable() {
-            Self::set_main_task(None, self.ch.number());
-        }
-
-        #[cfg(not(feature = "nrf51"))]
-        if TASK_COUNT == 1 && !C::configurable() || TASK_COUNT == 2 {
-            Self::set_fork_task(None, self.ch.number());
-        }
-
-        if EVENT_COUNT == 1 {
-            Self::set_event(None, self.ch.number());
+        Self {
+            ch,
+            phantom: PhantomData,
         }
     }
 }
+
+impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
+    pub fn new_one_to_one(ch: impl Unborrow<Target = C> + 'd, event: Event, task: Task) -> Self {
+        unborrow!(ch);
+
+        let r = unsafe { &*pac::PPI::ptr() };
+        let n = ch.number();
+        r.ch[n].eep.write(|w| unsafe { w.bits(event.reg_val()) });
+        r.ch[n].tep.write(|w| unsafe { w.bits(task.reg_val()) });
+
+        Self {
+            ch,
+            phantom: PhantomData,
+        }
+    }
+}
+
+#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
+impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
+    pub fn new_one_to_two(
+        ch: impl Unborrow<Target = C> + 'd,
+        event: Event,
+        task1: Task,
+        task2: Task,
+    ) -> Self {
+        unborrow!(ch);
+
+        let r = unsafe { &*pac::PPI::ptr() };
+        let n = ch.number();
+        r.ch[n].eep.write(|w| unsafe { w.bits(event.reg_val()) });
+        r.ch[n].tep.write(|w| unsafe { w.bits(task1.reg_val()) });
+        r.fork[n].tep.write(|w| unsafe { w.bits(task2.reg_val()) });
+
+        Self {
+            ch,
+            phantom: PhantomData,
+        }
+    }
+}
+
+impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop
+    for Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
+{
+    fn drop(&mut self) {
+        self.disable();
+
+        let r = unsafe { &*pac::PPI::ptr() };
+        let n = self.ch.number();
+        r.ch[n].eep.write(|w| unsafe { w.bits(0) });
+        r.ch[n].tep.write(|w| unsafe { w.bits(0) });
+        r.fork[n].tep.write(|w| unsafe { w.bits(0) });
+    }
+}