<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://devzone.nordicsemi.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>nrfx: GRTC driver, syscounter with compare events and GPPI/DPPI</title><link>https://devzone.nordicsemi.com/f/nordic-q-a/127273/nrfx-grtc-driver-syscounter-with-compare-events-and-gppi-dppi</link><description>Hello, 
 I am trying to get to grips with the nrfx driver: GRTC. 
 I want to create the following sequence with the lowest possible power consumption. Activate output 1 for 100 ms, sleep for 30 seconds, then perform an action on output 2 for 100 ms. </description><dc:language>en-US</dc:language><generator>Telligent Community 13</generator><lastBuildDate>Fri, 06 Mar 2026 00:49:31 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://devzone.nordicsemi.com/f/nordic-q-a/127273/nrfx-grtc-driver-syscounter-with-compare-events-and-gppi-dppi" /><item><title>RE: nrfx: GRTC driver, syscounter with compare events and GPPI/DPPI</title><link>https://devzone.nordicsemi.com/thread/562692?ContentTypeID=1</link><pubDate>Fri, 06 Mar 2026 00:49:31 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:edd4f7ce-4ac7-445b-9c5b-d928cf4beadf</guid><dc:creator>Kenneth</dc:creator><description>&lt;p&gt;I did play around a bit with this in case it can be of use.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/kemegard/generic_grtc/blob/main/src/main.c"&gt;https://github.com/kemegard/generic_grtc/blob/main/src/main.c&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Kenneth&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: nrfx: GRTC driver, syscounter with compare events and GPPI/DPPI</title><link>https://devzone.nordicsemi.com/thread/562671?ContentTypeID=1</link><pubDate>Thu, 05 Mar 2026 16:35:48 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:6a697695-e1c4-4f6b-8580-2df7d309c164</guid><dc:creator>Gaby_inno</dc:creator><description>&lt;p&gt;Hello,&lt;/p&gt;
&lt;p&gt;Thank you for your reply.&lt;br /&gt;That&amp;#39;s interesting information.&lt;/p&gt;
&lt;p&gt;You&amp;#39;re right about that: for the first activation of output 1 for 100 ms, using GRTC won&amp;#39;t save us much in terms of power consumption. &lt;br /&gt;So we can leave it on timer20 as in my first test.&lt;/p&gt;
&lt;p&gt;It is for the 30-second sleep that it is important to use GRTC to have the event compare in system OFF mode and save on power consumption. &lt;br /&gt;And this part can work without STOP or RESET.&lt;/p&gt;
&lt;p&gt;We do not want to use the kernel timers because in our use case we must be real-time. &lt;br /&gt;We cannot afford to have jitter on the 100ms time, output 1 or 2.&lt;br /&gt;The precision over 30 seconds is less important.&lt;/p&gt;
&lt;p&gt;Output 1 at high level for 100ms &amp;rArr;&amp;nbsp;on channel compare0 timer20@1MHz (121uA @ 3V) this power consumption level will be for 100ms&lt;br /&gt;30-second pause &amp;rArr; on channel compareX GRTC@1MHz, we will have low consumption when I put the system in OFF mode. &lt;br /&gt;Output 2 at high level for 100ms&amp;nbsp;&amp;rArr; on channel compareX GRTC@1MHz, we will have low consumption when I put the system in OFF mode.&lt;/p&gt;
&lt;p&gt;Here is my code with a mixed functional solution (timer + GRTC) :&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;#include &amp;lt;zephyr/kernel.h&amp;gt;
#include &amp;lt;zephyr/device.h&amp;gt;
#include &amp;lt;zephyr/drivers/gpio.h&amp;gt;
#include &amp;lt;zephyr/sys/printk.h&amp;gt;
#include &amp;lt;zephyr/sys/__assert.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;stdbool.h&amp;gt;
#include &amp;quot;hal/nrf_timer.h&amp;quot;
#include &amp;quot;hal/nrf_gpio.h&amp;quot;
#include &amp;quot;hal/nrf_grtc.h&amp;quot;
#include &amp;lt;nrfx_gpiote.h&amp;gt;
#if defined(CONFIG_GPIO)
#include &amp;lt;gpiote_nrfx.h&amp;gt;
#endif
#include &amp;quot;nrfx_timer.h&amp;quot;
#include &amp;quot;nrfx_grtc.h&amp;quot;
#include &amp;lt;helpers/nrfx_gppi.h&amp;gt;

#include &amp;quot;main.h&amp;quot;
#include &amp;quot;motor.h&amp;quot;


#define OUTPUT_PIN0  0  // P0.00
#define OUTPUT_PIN1  1  // P0.01

#define OPEN_PIN     NRF_GPIO_PIN_MAP(0, OUTPUT_PIN0)   // Your OUTPUT_PIN0
#define CLOSE_PIN    NRF_GPIO_PIN_MAP(0, OUTPUT_PIN1)   // Your OUTPUT_PIN1

#define TIMER_INST   20

#define GPIOTE_NODE	DT_NODELABEL(gpiote30)

// define time in ticks (assuming 1MHz timer frequency for easy conversion to microseconds)
#define TICKS_0MS_1MHZ 1 // 0ms
#define TICKS_100MS_1MHZ 100000 // 100ms at 1MHz
#define TICKS_30S_1MHZ 30000000 // 30s at 1MHz
#define MOTOR_TIMER20_FREQ_1MHz 1000000UL

#define TICK_100MS (TICKS_0MS_1MHZ + TICKS_100MS_1MHZ) // 100ms after the first event
#define TICK_30S (TICK_100MS + TICKS_30S_1MHZ) // 30s after the first event
#define TICK_30S_100MS (TICK_30S + TICKS_100MS_1MHZ) // 30s + 100ms after the first event

// 
#if !defined(CONFIG_GPIO)
static nrfx_gpiote_t gpiote_node = NRFX_GPIOTE_INSTANCE(NRF_GPIOTE_INST_GET(GPIOTE_NODE));
static nrfx_gpiote_t *gpiote_inst = &amp;amp;gpiote_node;
#else
static nrfx_gpiote_t *gpiote_inst = &amp;amp;GPIOTE_NRFX_INST_BY_NODE(GPIOTE_NODE);
#endif
static nrfx_timer_t timer = NRFX_TIMER_INSTANCE(NRF_TIMER_INST_GET(TIMER_INST));

static uint8_t GRTC_channel_0 = 0; // Channel for CLOSE_PIN
static uint8_t GRTC_channel_1 = 0; // Channel for CLOSE_PIN

static void grtc_compare_set_GPPI(void)
{
    
    nrfx_err_t rv;

    // free the channel in case it was already allocated (e.g. after a previous run of the test)
    if (GRTC_channel_0 &amp;gt; 0) 
    {
        rv = nrfx_grtc_channel_free(GRTC_channel_0);
        if (rv != 0) {
            log_debug(1, 60);
            return;
        }
    }

    if (GRTC_channel_1 &amp;gt; 0) 
    {
        rv = nrfx_grtc_channel_free(GRTC_channel_1);
        if (rv != 0) {
            log_debug(1, 60);
            return;
        }
    }

    rv = nrfx_grtc_channel_alloc(&amp;amp;GRTC_channel_0);
    if (rv != 0) {
        log_debug(1, 60);
        return;
    }

    // Configure the GRTC compare events with no interrupt, but with shortcuts at 0.001ms (1 tick) to set the OPEN_PIN
    rv = nrfx_grtc_syscounter_cc_relative_set(&amp;amp;(nrfx_grtc_channel_t){ .handler = NULL, .p_context = NULL, .channel = GRTC_channel_0 }, TICK_30S, true,
						     NRFX_GRTC_CC_RELATIVE_SYSCOUNTER);
    if (rv != 0) {
        log_debug(1, 70);
        return 0;
    }

    rv = nrfx_grtc_channel_alloc(&amp;amp;GRTC_channel_1);
    if (rv != 0) {
        log_debug(1, 60);
        return;
    }

    // Configure the GRTC compare events with no interrupt, but with shortcuts at 100ms to clear the OPEN_PIN
    rv = nrfx_grtc_syscounter_cc_relative_set(&amp;amp;(nrfx_grtc_channel_t){ .handler = NULL, .p_context = NULL, .channel = GRTC_channel_1 }, TICK_30S_100MS, true,
						     NRFX_GRTC_CC_RELATIVE_SYSCOUNTER);
    if (rv != 0) {
        log_debug(1, 72);
        return 0;
    }
}

void motor_set_move(void) {
    
	// Trigger SET tasks timer to start the sequence (OPEN at 100ms, CLOSE at 30s, stop at 30s + 100ms)
    grtc_compare_set_GPPI();
    nrf_timer_task_trigger(timer.p_reg, NRF_TIMER_TASK_START);
    // DPPI/GPPI will take care of the rest (clear after 100ms, set after 30s, clear after 30s + 100ms)

    log_debug(1, 20);
}


nrfx_gpiote_t *motor_get_gpiote_inst_ptr(void)
{
    return gpiote_inst;
}

/**
 * @brief Thread to manage the motor
 * 
 */
void main_motor(void)
{
	nrfx_err_t rv;
	uint8_t out_channel0, out_channel1;
    
    log_debug(1, 0);

    /*******************
    *   GPIO
    *******************/

    // Configure as outputs manually if needed (before GPIOTE config)
    nrf_gpio_cfg_output(OPEN_PIN);
    nrf_gpio_cfg_output(CLOSE_PIN);

    /*******************
    *   GPIOTE
    *******************/

    /* Initialisation GPIOTE (&amp;#224; faire une seule fois dans l’appli) */
    if (!nrfx_gpiote_init_check(gpiote_inst)) {
        rv = nrfx_gpiote_init(gpiote_inst,NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY);
        if (rv != 0) {
            log_debug(1, 1);
            return;
        }
    }

	// Allocate GPIOTE channels 0 and 1 for the two output pins 
    rv = nrfx_gpiote_channel_alloc(gpiote_inst, &amp;amp;out_channel0);
    if (rv != 0) {
        log_debug(1, 2);
        return;
    }

    // Allocate another GPIOTE channel for the second output
    rv = nrfx_gpiote_channel_alloc(gpiote_inst, &amp;amp;out_channel1);
    if (rv != 0) {
        log_debug(1, 3);
        return;
    }
    
    // Configure the output pins with GPIOTE, pull-up disabled, standard drive
	nrfx_gpiote_output_config_t output_config = {
	    .drive = NRF_GPIO_PIN_S0S1,
        .input_connect = NRF_GPIO_PIN_INPUT_DISCONNECT,
        .pull = NRF_GPIO_PIN_NOPULL
	};

    // Task configuration for the first pin (OPEN_PIN): task toogle on compare event, initial value low
    nrfx_gpiote_task_config_t task_config0 =
    {
        .task_ch = out_channel0,
        .polarity = NRF_GPIOTE_POLARITY_LOTOHI,
        .init_val = NRF_GPIOTE_INITIAL_VALUE_LOW,
    };

    // Task configuration for the second pin (CLOSE_PIN): task toogle on compare event, initial value low
    nrfx_gpiote_task_config_t task_config1 =
    {
        .task_ch = out_channel1,
        .polarity = NRF_GPIOTE_POLARITY_LOTOHI,
        .init_val = NRF_GPIOTE_INITIAL_VALUE_LOW,
    };

    // configure OPEN_PIN pin with task config in GPIOTE
    rv = nrfx_gpiote_output_configure(gpiote_inst, OPEN_PIN, &amp;amp;output_config, &amp;amp;task_config0);
    if (rv != 0) {
        log_debug(1, 4);
        return;
    }
 
    // Configure CLOSE_PIN pin with task config in GPIOTE
    rv = nrfx_gpiote_output_configure(gpiote_inst, CLOSE_PIN, &amp;amp;output_config, &amp;amp;task_config1);
    if (rv != 0) {
        log_debug(1, 5);
        return;
    }

    // Enable the tasks for both pins
    nrfx_gpiote_out_task_enable(gpiote_inst, OPEN_PIN);
    nrfx_gpiote_out_task_enable(gpiote_inst, CLOSE_PIN);

    /*******************
    *   TIMER
    *******************/

    // Initialize Timer
    nrfx_timer_config_t timer_cfg = {
        .frequency = MOTOR_TIMER20_FREQ_1MHz, // 1MHz for low consumption 
        .mode = NRF_TIMER_MODE_TIMER,
        .bit_width = NRF_TIMER_BIT_WIDTH_32,
        .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY,
        .p_context = NULL
    };

    // Check if timer is already initialized, if not initialize it
    if (!nrfx_timer_init_check(&amp;amp;timer)) {
        rv = nrfx_timer_init(&amp;amp;timer, &amp;amp;timer_cfg, NULL);  // No IRQ handler
        if (rv != 0) {
            log_debug(1, 6);
            log_debug(6, rv);
            return 0;
        }
    }

    // Configure the timer compare events with no interrupt, but with shortcuts at 0.001ms (1 tick) to set the OPEN_PIN
    nrfx_timer_extended_compare(&amp;amp;timer, NRF_TIMER_CC_CHANNEL0, TICKS_0MS_1MHZ,
                                0 ,
                                false);  // No interrupt
    // Configure the timer compare events with no interrupt, but with shortcuts at 100ms to clear the OPEN_PIN
    nrfx_timer_extended_compare(&amp;amp;timer, NRF_TIMER_CC_CHANNEL1, TICK_100MS,
                                NRF_TIMER_SHORT_COMPARE1_STOP_MASK | NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK,
                                false);  // No interrupt

    /*******************
    *   GRTC
    *******************/
   
    // initialize GRTC (if not already initialized, can be done in another thread if needed)
    if (!nrfx_grtc_init_check()) {
        rv = nrfx_grtc_init(NRFX_GRTC_DEFAULT_CONFIG_IRQ_PRIORITY);
        if (rv != 0) {
            log_debug(1, 6);
            return;
        }
    }

    grtc_compare_set_GPPI();

    /*******************
    *   DPPI/GPPI
    *******************/

    // DPPI/GPPI handles for the connections
    nrfx_gppi_handle_t gppi_handle_open, gppi_handle_clear_open, gppi_handle_pause30s, gppi_handle_close;

    // Get the addresses of the tasks and events to connect with DPPI/GPPI
    uint32_t set_task_addr0 = nrfx_gpiote_set_task_address_get(gpiote_inst, OUTPUT_PIN0);
    uint32_t clr_task_addr0 = nrfx_gpiote_clr_task_address_get(gpiote_inst, OUTPUT_PIN0);
    uint32_t set_task_addr1 = nrfx_gpiote_set_task_address_get(gpiote_inst, OUTPUT_PIN1);
    uint32_t clr_task_addr1 = nrfx_gpiote_clr_task_address_get(gpiote_inst, OUTPUT_PIN1);

    // Get the addresses of the timer compare events
    uint32_t eep = nrfx_timer_compare_event_address_get(&amp;amp;timer, NRF_TIMER_CC_CHANNEL0);
    uint32_t eep2 = nrfx_timer_compare_event_address_get(&amp;amp;timer, NRF_TIMER_CC_CHANNEL1);
    uint32_t eep3 = nrfx_grtc_event_compare_address_get(GRTC_channel_0);
    uint32_t eep4 = nrfx_grtc_event_compare_address_get(GRTC_channel_1);


    // Allocate a GPPI channel and connect the timer event to the first task : stop OPEN at 100ms (one-to-one)
    rv = nrfx_gppi_conn_alloc(eep, set_task_addr0, &amp;amp;gppi_handle_open);
    if (rv != 0) {
        log_debug(1, 7);
        return 0;
    }

    // Allocate a GPPI channel and connect the timer event to the first task : stop OPEN at 100ms (one-to-one)
    rv = nrfx_gppi_conn_alloc(eep2, clr_task_addr0, &amp;amp;gppi_handle_clear_open);
    if (rv != 0) {
        log_debug(1, 8);
        return 0;
    }

    // Allocate a GPPI channel and connect the timer event to the second task : start CLOSE at 30s (one-to-one)
    rv = nrfx_gppi_conn_alloc(eep3, set_task_addr1, &amp;amp;gppi_handle_pause30s);
    if (rv != 0) {
        log_debug(1, 9);
        return 0;
    }

    // Allocate a GPPI channel and connect the timer event to the third task : stop CLOSE at 30s + 100ms (one-to-one)
    rv = nrfx_gppi_conn_alloc(eep4, clr_task_addr1, &amp;amp;gppi_handle_close);
    if (rv != 0) {
        log_debug(1, 10);
        return 0;
    }

    // Enable the GPPI channels
    nrfx_gppi_conn_enable(gppi_handle_open);
    nrfx_gppi_conn_enable(gppi_handle_clear_open);
    nrfx_gppi_conn_enable(gppi_handle_pause30s);
    nrfx_gppi_conn_enable(gppi_handle_close);

    log_debug(1, 11);
    
    // start the timer to trigger the sequence of events (0.001ms, 100ms, 30s, 30s+100ms)
    nrf_timer_task_trigger(timer.p_reg, NRF_TIMER_TASK_START);

	//motor controle handler loop
	while (1) {
		k_msleep(100);
	}

}

K_THREAD_DEFINE(motor_id, STACKSIZE, main_motor, NULL, NULL, NULL, PRIORITY, 0, 0); // gestion du moteur a implementer&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;I still need to implement the system off part, but I don&amp;#39;t see any issues with that.&lt;/p&gt;
&lt;p&gt;thank you !&lt;/p&gt;
&lt;p&gt;Gaby&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: nrfx: GRTC driver, syscounter with compare events and GPPI/DPPI</title><link>https://devzone.nordicsemi.com/thread/562653?ContentTypeID=1</link><pubDate>Thu, 05 Mar 2026 14:00:25 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:f44affe4-1a5a-41a9-87d8-5f3f0635454a</guid><dc:creator>Kenneth</dc:creator><description>&lt;p&gt;Hello again,&lt;/p&gt;
&lt;p&gt;The syscounter will only reset on power-on reset, brown-out reset and pin reset. For other reset sources the syscounter is retained. There is no way to clear the syscounter by other means.&lt;/p&gt;
&lt;p&gt;Kenneth&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: nrfx: GRTC driver, syscounter with compare events and GPPI/DPPI</title><link>https://devzone.nordicsemi.com/thread/562581?ContentTypeID=1</link><pubDate>Wed, 04 Mar 2026 17:54:48 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:2ab069d0-08fb-496c-ab38-a63b762d2dd1</guid><dc:creator>Kenneth</dc:creator><description>&lt;p&gt;Hello,&lt;/p&gt;
&lt;p&gt;I can try to take a look, but I would like to mention&amp;nbsp;that I don&amp;#39;t expect much&amp;nbsp;power benefit of using nrfx for this. A simple k_msleep() or use&amp;nbsp;&lt;span&gt;k_timer_start() to execute something every&lt;/span&gt;&amp;nbsp;30seconds will do the job here. Using that api you can expect a sleep current of ~1-3uA when CPU is not executing code while waiting to wakeup.&lt;/p&gt;
&lt;p&gt;With the implementation you have used until now you have been using TIMER (121-450uA @ 3V):&lt;br /&gt;&lt;a href="https://docs.nordicsemi.com/bundle/ps_nrf54L15/page/chapters/current_consumption/doc/current_consumption.html#ariaid-title12"&gt;https://docs.nordicsemi.com/bundle/ps_nrf54L15/page/chapters/current_consumption/doc/current_consumption.html#ariaid-title12&lt;/a&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;By using the zephyr kernel api to for sleep (1.5-3.5uA @ 3V depending on the amount of RAM you retain):&lt;br /&gt;&lt;a href="https://docs.nordicsemi.com/bundle/ps_nrf54L15/page/chapters/current_consumption/doc/current_consumption.html#ariaid-title4"&gt;https://docs.nordicsemi.com/bundle/ps_nrf54L15/page/chapters/current_consumption/doc/current_consumption.html#ariaid-title4&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Edit: added links and fixed some text.&lt;/p&gt;
&lt;p&gt;Kenneth&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>