Electronics Design AU
nRF

How Does PPI/DPPI Work on the nRF52 and nRF53?

Last updated 3 July 2026 · 8 min read

Direct Answer

PPI (Programmable Peripheral Interconnect) and its successor DPPI (Distributed PPI) let one nRF peripheral's hardware EVENT automatically trigger another peripheral's hardware TASK, entirely inside the chip's fabric, with zero CPU involvement and zero interrupt latency. The classic nRF52 (nRF52832, nRF52840) uses PPI: a fixed set of channels, each wired at configuration time to exactly one event and up to two tasks (the second via a "fork"). The nRF53 series (and newer nRF52 variants such as the nRF52820) use DPPI instead: a publish/subscribe model where any number of peripherals can publish to a channel and any number of tasks can subscribe to it, without the channel itself being a physical crossbar connection. Both let firmware build autonomous hardware chains — for example, a TIMER compare event triggering an SAADC sample, or a GPIOTE pin edge triggering an SPI transfer — that run correctly even while the CPU is asleep, which is central to nRF's low-power design story.

Detailed Explanation

Most MCU peripherals only talk to the CPU: a peripheral raises an interrupt, the CPU's ISR runs, and the ISR pokes a register on another peripheral to start the next step. That round trip costs interrupt latency and CPU wake-time on every single event — expensive in a design where the CPU is asleep most of the time, which is the normal operating mode for a battery-powered nRF52 or nRF53 product.

PPI and DPPI exist to remove the CPU from that loop entirely. Every nRF peripheral exposes two kinds of hardware signals in its register map:

  • EVENTS — a peripheral raises an event when something happens (a timer compare match, a GPIO pin edge, an ADC conversion finishing, a UART byte received).
  • TASKS — a peripheral starts doing something when a task is triggered (start sampling, start a DMA transfer, toggle a pin, start a UART transmission).

Normally, the only way to connect an EVENT to a TASK is for firmware to observe the event (via polling or an interrupt) and then write to the task register. PPI/DPPI instead wires the event directly to the task in hardware, so the connection fires with no CPU involvement and effectively no software latency.


PPI (nRF52832, nRF52840): Fixed Channels, Event → Task(+Fork)

Classic PPI is a crossbar of fixed hardware channels. Each channel has exactly two endpoint registers that firmware configures once at startup:

  • CH[n].EEP (Event End Point) — the address of the EVENTS register that triggers the channel.
  • CH[n].TEP (Task End Point) — the address of the TASKS register the channel triggers.
/* nRF52, nrfx_ppi driver: TIMER0 COMPARE0 event triggers SAADC SAMPLE task */
nrf_ppi_channel_t ppi_channel;
nrfx_ppi_channel_alloc(&ppi_channel);
nrfx_ppi_channel_assign(ppi_channel,
                         nrfx_timer_compare_event_address_get(&timer0, NRF_TIMER_CC_CHANNEL0),
                         nrf_saadc_task_address_get(NRF_SAADC_TASK_SAMPLE));
nrfx_ppi_channel_enable(ppi_channel);

Each channel also has an optional fork — a second task endpoint (FORK[n].TEP) that fires alongside the primary task from the same event, at no extra channel cost. A single TIMER compare event can, for example, simultaneously trigger an SAADC sample and toggle a GPIOTE output pin for an external trigger signal, using one channel.

Channel count is fixed and limited. The exact number of programmable channels — and how many additional "pre-programmed" channels are reserved by Nordic for internal SoftDevice or radio-driver use — varies by part; check the specific device's product specification rather than assuming a number. Channels are a genuinely scarce resource on designs that chain several peripherals together, and channel groups (NRF_PPI->CHG[n]) let firmware enable or disable several channels at once as a set, which is useful for switching an entire autonomous chain on or off in one operation.


DPPI (nRF53, nRF52820, and newer silicon): Publish/Subscribe

DPPI replaces the fixed point-to-point crossbar with a publish/subscribe model. Instead of a central channel table holding both endpoints, each peripheral's own EVENTS and TASKS registers gain a PUBLISH_<event> or SUBSCRIBE_<task> register that names a channel number directly:

/* nRF53, nrfx_dppi driver: TIMER0 COMPARE0 event publishes to a channel; SAADC SAMPLE subscribes to it */
uint8_t dppi_channel;
nrfx_dppi_channel_alloc(&dppi_channel);

nrf_timer_publish_set(timer0.p_reg, NRF_TIMER_EVENT_COMPARE0, dppi_channel);
nrf_saadc_subscribe_set(NRF_SAADC, NRF_SAADC_TASK_SAMPLE, dppi_channel);

nrfx_dppi_channel_enable(dppi_channel);

The practical difference: DPPI is natively many-to-many. Any number of peripherals can publish to the same channel, and any number of tasks can subscribe to it — there is no equivalent of PPI's two-endpoints-per-channel limit, and no separate "fork" concept is needed since any additional subscriber is just another SUBSCRIBE register pointed at the same channel number.

nrfx_gppi — writing portable code across PPI and DPPI silicon. Because PPI and DPPI have different APIs, Nordic's nrfx layer provides nrfx_gppi (Generic PPI) as a thin wrapper that exposes one API and compiles to the correct backend for the target part. Application and driver code written against nrfx_gppi_channel_alloc(), nrfx_gppi_event_endpoint_setup(), nrfx_gppi_task_endpoint_setup(), and nrfx_gppi_channels_enable() runs unmodified on both an nRF52840 (PPI) and an nRF5340 (DPPI) build target — this is the recommended API for any new code, including under Zephyr and the nRF Connect SDK, rather than calling nrfx_ppi or nrfx_dppi directly.


Common Use Cases

  • Timer-triggered ADC sampling with the CPU asleep. A TIMER compare event chained to the SAADC SAMPLE task produces evenly spaced samples at a hardware-guaranteed interval, with the CPU only waking on the SAADC END event (or not at all, if results are handled by EasyDMA into a buffer and only inspected periodically). This removes the jitter of a software-timed sampling loop and avoids waking the CPU for every sample.
  • GPIOTE-triggered peripheral response to an external edge. A GPIOTE IN event on an external interrupt pin can directly trigger a SPI, UART, or TWI (I2C) task — starting a transfer the instant a pin edge occurs, with no interrupt-handler latency between the edge and the peripheral starting.
  • Waveform and pulse generation. Chaining an RTC or TIMER compare event to a GPIOTE OUT task toggles a pin entirely in hardware on a timer schedule — useful for simple waveform generation or driving an external device's clock/strobe line without CPU intervention on every edge.
  • Multi-step autonomous sequences. SAADC's own END event can be chained via PPI/DPPI back into another peripheral's task, letting firmware build small autonomous state machines — sample, then trigger a comparator, then trigger a GPIO action — that execute correctly across CPU sleep cycles.
  • EGU (Event Generator Unit) as a software-triggered event source. The EGU peripheral lets firmware trigger a PPI/DPPI channel from a plain register write, rather than from a hardware peripheral event — useful for software-initiated chains that still need to fan out to a task via the same interconnect fabric as hardware-triggered ones.

This directly supports the power-optimisation techniques covered in nRF52 Power Optimisation: every event-to-task chain built in PPI/DPPI is one fewer reason to wake the CPU, and CPU wake time is the dominant cost in most nRF low-power budgets.


Design Considerations

  • Budget channels early on multi-peripheral designs. PPI's fixed channel count (and DPPI's practical channel count, which is also finite even though the model is many-to-many) is easy to exhaust once a design chains several sensors, timers, and GPIO triggers together. Sketch the intended event/task graph before implementation, not after running out of channels.
  • Some channels may already be claimed by the SDK or SoftDevice. On classic PPI parts, a subset of channels can be reserved by Nordic's SoftDevice or by SDK drivers (such as certain radio timing paths) before application code ever runs. Always allocate channels through the nrfx_ppi/nrfx_dppi/nrfx_gppi allocator functions rather than hardcoding a channel index, so the allocator can correctly avoid channels already in use.
  • Fork (PPI) vs. multiple subscribers (DPPI) is not a drop-in equivalence when porting. Code written for classic PPI's single-fork-per-channel model needs restructuring, not just an API rename, when porting to DPPI's native many-to-many model — and vice versa. Using nrfx_gppi from the start avoids this rework entirely for new designs that may need to target both nRF52 and nRF53 hardware.
  • PPI/DPPI connects hardware events to hardware tasks — it does not replace interrupts for application logic. It is the right tool for "peripheral A's signal should directly start peripheral B's action," not for general application control flow, which still belongs in interrupt handlers and application threads.

Common Mistakes

  • Assuming every nRF52 part uses the same interconnect. Code and reference designs that hardcode nrfx_ppi calls will not build correctly against an nRF52820 or nRF53 target, which use DPPI. Write new code against nrfx_gppi unless the design is permanently committed to a single silicon family.
  • Forgetting to enable the channel after configuring endpoints. Both PPI and DPPI require an explicit enable step (nrfx_ppi_channel_enable() / nrfx_dppi_channel_enable(), or the equivalent CHENSET register write) after the event/task endpoints are configured — a channel with valid endpoints but no enable simply never fires, which is easy to misdiagnose as a peripheral configuration fault rather than a missing one-line call.
  • Not accounting for channels reserved by other SDK components. Enabling BLE (SoftDevice or the Zephyr BLE stack) or the 802.15.4 radio driver can silently consume PPI/DPPI channels before application code runs. A design that allocates channels by hardcoded index rather than through the driver's allocator can collide with these reserved channels in ways that only surface intermittently.
  • Expecting PPI/DPPI events to be visible to the debugger as a call stack. Because the connection is a pure hardware signal path with no software function call, a debugger breakpoint cannot be set "inside" a PPI/DPPI-triggered task the way it can inside an ISR. Debugging an autonomous chain generally requires toggling a spare GPIOTE-driven pin on each step and observing it with a logic analyser or oscilloscope, or using RTT logging in the eventual completion handler once the CPU does wake.
  • Treating the fork (PPI) as unlimited. Classic PPI's fork gives exactly one extra task per channel — it is not a general fan-out mechanism. A design that needs one event to trigger three or more tasks needs either DPPI's native multi-subscriber model or multiple separate PPI channels all wired to the same event.

Frequently Asked Questions

Does my nRF52 use PPI or DPPI?
The nRF52832 and nRF52840 — the two most widely used nRF52 devices — use classic PPI. The nRF52820 and the entire nRF53 series (nRF5340) use DPPI. Always confirm against the specific part's product specification rather than assuming by family name, since Nordic introduced DPPI partway through the nRF52 generation on selected newer parts before making it standard on nRF53 and later silicon.
Do I need to use PPI/DPPI directly, or does the SDK handle it for me?
Some driver-level functionality (such as certain SAADC continuous-sampling modes and the nRF 802.15.4 radio driver's timing-critical paths) configures PPI/DPPI channels internally without application code needing to touch them. For custom autonomous chains — such as a specific sensor's timer-triggered sampling scheme — application firmware typically allocates and configures channels directly, either through the low-level nrfx_ppi/nrfx_dppi driver or the nrfx_gppi portable wrapper.
Can PPI/DPPI trigger a task while the CPU is in System ON sleep?
Yes — that's the primary reason to use it. PPI/DPPI operates entirely within the peripheral fabric; it does not require the CPU core to be running. A TIMER-to-SAADC PPI chain continues sampling on schedule while the CPU is in System ON CPU sleep between BLE events, which is the mechanism behind ultra-low-power sensor polling designs. See nRF52 Power Optimisation for how this fits into an overall low-power design.

References

Related Questions

Related Forum Discussions