Xiao BLE nrf52840 Zephyr PPI / GPIOTE

At the moment i am trying to port my Arduino code to Zephyr OS. So i am a complete noob in this area. Can someone give me a clue where i can find documentation about the nrfx, ppi and gpiote parts ?

I am trying to port this and can’t find docs or a working example…

#include <Arduino.h>

#define OUTPUT_PIN 44 //

void setup() {Serial.begin(115200);while (!Serial);

pinMode(OUTPUT_PIN, OUTPUT);
digitalWrite(OUTPUT_PIN, LOW);

// 🛠️ GPIOTE 
NRF_GPIOTE->CONFIG[0] =
    (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
    (OUTPUT_PIN << GPIOTE_CONFIG_PSEL_Pos) |
    (GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos) |
    (GPIOTE_CONFIG_OUTINIT_Low << GPIOTE_CONFIG_OUTINIT_Pos);

// 🛠️ Timer 
NRF_TIMER3->MODE = TIMER_MODE_MODE_Timer;
NRF_TIMER3->BITMODE = TIMER_BITMODE_BITMODE_16Bit;
NRF_TIMER3->PRESCALER = 4;
NRF_TIMER3->CC[0] = 10000;   // 10 ms Puls
NRF_TIMER3->CC[1] = 50000;   // 50 ms Pause

// 🛠️ PPI 
NRF_PPI->CH[0].EEP = (uint32_t)&NRF_TIMER3->EVENTS_COMPARE[0];  // Timer CC[0] → High
NRF_PPI->CH[0].TEP = (uint32_t)&NRF_GPIOTE->TASKS_SET[0];

NRF_PPI->CH[1].EEP = (uint32_t)&NRF_TIMER3->EVENTS_COMPARE[1];  // Timer CC[1] → Low
NRF_PPI->CH[1].TEP = (uint32_t)&NRF_GPIOTE->TASKS_CLR[0];

// PPI channel
NRF_PPI->CHENSET = (1 << 0) | (1 << 1);

// 🛠️ Timer reset after cc[1]
NRF_TIMER3->SHORTS = TIMER_SHORTS_COMPARE1_CLEAR_Msk;

// Timer
NRF_TIMER3->TASKS_START = 1;

Serial.println("Timer + PPI + GPIOTE startet");

}

void loop() {
// Nothing to do, Everthing works in HW
}

Hi there,

And Welcome here…

So , this The Official Zephyr nrfx Sample (The Holy Grail)

Zephyr includes an official, out-of-the-box code sample specifically designed to demonstrate how to use nrfx drivers alongside GPIOTE and PPI/DPPI hardware interconnects without CPU intervention.

Dealing with the “Zephyr vs. NRFX Conflict”

One of the first brick walls an Arduino developer hits when porting code to Zephyr is that Zephyr’s driver initialization automatically claims GPIOTE channels behind the scenes for its own interrupt handling. If they call bare-metal nrfx_gpiote_init(), it will clash and cause compile or runtime errors.

Share this fantastic community deep-dive from the Nordic DevZone:

This explains how to let Zephyr configure the pins via regular DeviceTree/GPIO APIs, and then cleanly bridge them to PPI hardware routing using nrfx_gppi_channel_endpoints_setup() by pulling the raw event addresses like this:

nrfx_gppi_channel_endpoints_setup(
    ppi_channel, 
    nrfx_gpiote_in_event_address_get(&gpiote, INPUT_PIN), 
    nrfx_gpiote_out_task_address_get(&gpiote, OUTPUT_PIN)
);

and Lastly…

Reference Source Code & Hardware Abstraction

If they just need to look at how the underlying functions are structured to replace their direct NRF_GPIOTE->CONFIG or NRF_PPI->CH registry assignments:

HTH
GL :slight_smile: PJ :v:

Post up any specific questions, also consult the TRG for the Nrf52840 it has more in it than the basic Data sheet. :+1:

Alright, thanks for your answer. I am digging into the stuff and get back with specific questions.

But due to your answer, i have a lot to read. :rofl:

Maybe i get it up and running by myself.

Hi there,

If you want to know the nuance it takes a little extra digging. One tip i will pass to you , is often overlooked “Power Domain” in the silicon die some GPIO groups are powered internally by different parts and sections of the PPI is also important to understand in General, For example different GPIO’s for sleep and wake up pins often need to be part of the same power domain in the mcu like the RTC also, for timer interrupts etc.

Also I noted the Technical Reference Guide for the Nordic parts and some of espressif parts too have those beyond the normal data sheet. they cover more in depth the specifics.

HTH
GL :slight_smile: PJ :v: