<?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>GPIOTE trigger of SPIM read using EasyDMA with manual CS</title><link>https://devzone.nordicsemi.com/f/nordic-q-a/129009/gpiote-trigger-of-spim-read-using-easydma-with-manual-cs</link><description>I’m using NRF Connect SDK 3.2.4 with the nRF54L15-DK. I’ve successfully used the nrfx_gppi_conn_alloc() and nrfx_gppi_conn_enable() APIs to connect a timer to a GPIOTE output. That output is on P1.12 and it drives ADC conversion control to an ADC IC.</description><dc:language>en-US</dc:language><generator>Telligent Community 13</generator><lastBuildDate>Thu, 27 Aug 2026 11:05:07 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://devzone.nordicsemi.com/f/nordic-q-a/129009/gpiote-trigger-of-spim-read-using-easydma-with-manual-cs" /><item><title>RE: GPIOTE trigger of SPIM read using EasyDMA with manual CS</title><link>https://devzone.nordicsemi.com/thread/570661?ContentTypeID=1</link><pubDate>Thu, 27 Aug 2026 11:05:07 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:81bc83cd-7d8b-4dec-bb77-7f8b614253da</guid><dc:creator>Pallavi Patil</dc:creator><description>&lt;p&gt;Hie,&amp;nbsp;&lt;br /&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span&gt;If this is a continuous acquisition, add NRFX_SPIM_FLAG_REPEATED_XFER together with NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER, so each DRDY re-runs the same held transfer without you re-arming it every time. Without that you get exactly one read and then silence&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;if the CS is still the manual GPIOTE one, the same DRDY edge should both pull CS low and start the SPIM, which stays a single handle, you just attach the second task with nrfx_gppi_ep_attach()&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Regards&lt;/p&gt;
&lt;p&gt;Pallavi&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: GPIOTE trigger of SPIM read using EasyDMA with manual CS</title><link>https://devzone.nordicsemi.com/thread/570641?ContentTypeID=1</link><pubDate>Wed, 26 Aug 2026 15:30:37 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:a251357a-4261-4742-bbf5-52ed9720e031</guid><dc:creator>Jason0123456</dc:creator><description>&lt;p&gt;Thanks for the response Pallavi. If you look at the drdy_init() routine in the relevant code snippet above you will note that I&amp;rsquo;ve performed each of your recommendations.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;em&gt;GPIOTE Task/Event Enables:&amp;nbsp;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;CS Pin: Once you configure the CS pin as an output using nrfx_gpiote_output_configure(), make sure to call nrfx_gpiote_out_task_enable() on the CS pin.&lt;/em&gt; &lt;strong&gt;(see line 139).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;em&gt;DRDY Pin: The IN event won&amp;#39;t generate until you execute nrfx_gpiote_trigger_enable(inst, DRDY_PIN, false).&lt;/em&gt;&lt;strong&gt; (see line 63)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Also, please check the parameter order in&amp;nbsp; the nrfx_gppi_conn_alloc() function signature, the parameter order strictly expects the triggering event address first, followed by the target task address second. Passing them in reverse order won&amp;#39;t trigger a compiler error since both are standard integer addresses, but it will map the signal routes backward, preventing the PPI channel from triggering.&amp;nbsp;&lt;strong&gt;(see line 122).&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;em&gt;SPIM CS Line Ownership:&amp;nbsp;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The next thing I would check is whether the SPIM is still holding the CS pin. With a manual CS you have to set ss_pin = NRF_SPIM_PIN_NOT_CONNECTED, otherwise the driver keeps driving the pin and ends up fighting the GPIOTE task.&lt;/em&gt; &lt;strong&gt;(see below).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;
	nrfx_spim_config_t spim_config = NRFX_SPIM_DEFAULT_CONFIG(SPIM_SCK_PIN,
                                                              SPIM_MOSI_PIN,
                                                              SPIM_MISO_PIN,
                                                              NRF_SPIM_PIN_NOT_CONNECTED);

	spim_config.frequency = 8000000;

	err = nrfx_spim_init(&amp;amp;spi_inst, &amp;amp;spim_config, NULL, NULL);
    if (err) {
        return 0;
    }	

	uint32_t flags = 0;	
	uint16_t reg_data = 0;

	nrf_gpio_cfg_output(SPIM_CS_PIN);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I would like to also note that I have a convst_init() routine that successfully uses the high-level gppi routines to connect a timer to a GPIOTE output. See below.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;pre class="ui-code" data-mode="text"&gt;// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------	
void convst_init(nrfx_gppi_handle_t* gppi_h) {

	int err;	
	
	uint8_t out_channel;		
	
	// ---------------------------------------
	// INIT GPIOTE
	// ---------------------------------------	
	if(!nrfx_gpiote_init_check(&amp;amp;gpiote_inst))
	{
		err = nrfx_gpiote_init(&amp;amp;gpiote_inst, NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY);
		if (err != 0) {
			LOG_ERR(&amp;quot;GPIOTE init fail: %x&amp;quot;, err);	
		}
		LOG_INF(&amp;quot;GPIOTE status: %s&amp;quot;, nrfx_gpiote_init_check(&amp;amp;gpiote_inst) ? &amp;quot;initialized&amp;quot; : &amp;quot;not initialized&amp;quot;);
	}

	// ---------------------------------------
	// CONVST Output
	// ---------------------------------------	
	err = nrfx_gpiote_channel_alloc(&amp;amp;gpiote_inst, &amp;amp;out_channel);
	if (err != 0) {
		LOG_ERR(&amp;quot;Channel alloc fail: %x&amp;quot;, err);	
	}
	
	nrfx_gpiote_output_config_t gpiote_out_cfg = {
		.drive = NRF_GPIO_PIN_S0S1,
		.input_connect = NRF_GPIO_PIN_INPUT_DISCONNECT,
		.pull = NRF_GPIO_PIN_NOPULL,
	};

	nrfx_gpiote_task_config_t gpiote_task_cfg = {
		.task_ch = out_channel,
		.polarity = NRF_GPIOTE_POLARITY_TOGGLE,
		.init_val = NRF_GPIOTE_INITIAL_VALUE_HIGH,
	};

	err = nrfx_gpiote_output_configure(&amp;amp;gpiote_inst, CONVST_PIN, &amp;amp;gpiote_out_cfg, &amp;amp;gpiote_task_cfg);
	if (err != 0) {
		LOG_ERR(&amp;quot;GPIOTE configure fail: %x&amp;quot;, err);	
	}

	nrfx_gpiote_out_task_enable(&amp;amp;gpiote_inst, CONVST_PIN);
	
	// ---------------------------------------
	// INIT TIMER
	// ---------------------------------------
	nrfx_timer_config_t timer_cfg = {
    	.frequency = NRFX_MHZ_TO_HZ(1), 
    	.mode = NRF_TIMER_MODE_TIMER,
    	.bit_width = NRF_TIMER_BIT_WIDTH_16,
    	.interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY,
	};    
	
   	err = nrfx_timer_init(&amp;amp;timer_inst, &amp;amp;timer_cfg, NULL);
     if (err != 0) {
		LOG_ERR(&amp;quot;TIMER init fail: %x&amp;quot;, err);	
	}

    nrfx_timer_clear(&amp;amp;timer_inst);   

    nrfx_timer_extended_compare(&amp;amp;timer_inst, NRF_TIMER_CC_CHANNEL1, 4, NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK, false);	

	// ---------------------------------------
	// 3. CONNECT GPIOTE w/ TIMER via GPPI
	// ---------------------------------------
	err = nrfx_gppi_conn_alloc(
        nrfx_timer_compare_event_address_get(&amp;amp;timer_inst, NRF_TIMER_CC_CHANNEL1),
        nrfx_gpiote_out_task_address_get(&amp;amp;gpiote_inst, CONVST_PIN), gppi_h);
    if (err != 0) {
		LOG_ERR(&amp;quot;GPPI channel allocation fail: %x&amp;quot;, err);	
	}	

    nrfx_timer_enable(&amp;amp;timer_inst);		  

	nrfx_gppi_conn_enable(*gppi_h);	
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Below is how I am calling each routine. I&amp;#39;m assuming I need two separate gppi handles since these are standalone and separate event/task and peripherals.&lt;/p&gt;
&lt;p&gt;Any additional thoughts on what I could be missing?&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;nrfx_gppi_handle_t drdy_gppi_h, convst_gppi_h;		
	
	// timer triggers conversion start (convst) to ADC 
	convst_init(&amp;amp;convst_gppi_h);	

	// drdy from ADC triggers SPI read of 15 samples (240 bytes)
	drdy_init(&amp;amp;drdy_gppi_h);		&lt;/pre&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: GPIOTE trigger of SPIM read using EasyDMA with manual CS</title><link>https://devzone.nordicsemi.com/thread/570619?ContentTypeID=1</link><pubDate>Wed, 26 Aug 2026 11:29:17 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:53182078-3d5c-48d2-aeb3-8c7352579c68</guid><dc:creator>Pallavi Patil</dc:creator><description>&lt;p&gt;Hie Jason,&lt;/p&gt;
&lt;p&gt;Thanks a lot for your patience.&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div dir="ltr"&gt;
&lt;p&gt;Your GPPI implementation looks solid to me. &lt;code&gt;nrfx_gppi_conn_alloc()&lt;/code&gt; and &lt;code&gt;nrfx_gppi_conn_enable()&lt;/code&gt; are the correct calls here they handle configuring the DPPI channel and managing the PPI bridge automatically if the endpoints cross power domains. I don&amp;#39;t suspect the connection logic itself. You should probably check if the&amp;nbsp;few initialization step have been implemented correctly&amp;nbsp;&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;GPIOTE Task/Event Enables:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;CS Pin:&lt;/b&gt; &lt;span&gt;Once you configure the CS pin as an output using &lt;/span&gt;&lt;code&gt;nrfx_gpiote_output_configure(),&lt;/code&gt;&amp;nbsp;make sure to call&amp;nbsp;&lt;code&gt;&lt;span&gt;nrfx_gpiote_out_task_enable()&amp;nbsp;&lt;/span&gt;&lt;/code&gt;on the CS pin&lt;code&gt;&lt;span&gt;.&lt;/span&gt;&lt;/code&gt;&amp;nbsp;Without this, the OUT task won&amp;#39;t drive the pin via PPI,&amp;nbsp;leading to the common issue of the hardware getting stuck in a half-configured state where the channel triggers&amp;nbsp;but&amp;nbsp;CS pin ignores it completely and never changes state.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span&gt;&lt;/span&gt;&lt;/code&gt;&lt;b&gt;DRDY Pin:&lt;/b&gt; The IN event won&amp;#39;t generate until you execute &lt;code&gt;nrfx_gpiote_trigger_enable(inst, DRDY_PIN, false)&lt;/code&gt;.&lt;span&gt;Setting the last parameter (&lt;/span&gt;&lt;code&gt;int_enable&lt;/code&gt;&lt;span&gt;) to &lt;/span&gt;&lt;code&gt;false&lt;/code&gt;&lt;span&gt; configures the hardware to generate the event locally without routing an interrupt to the CPU. This allows the PPI path to run entirely in hardware without invoking any software handlers or interrupting core execution.&lt;/span&gt;&lt;/p&gt;
&lt;span&gt;Also,please check&amp;nbsp;the parameter order in &amp;nbsp; the &lt;code&gt;nrfx_gppi_conn_alloc()&lt;/code&gt; function signature, the parameter order strictly expects the triggering event address first, followed by the target task address second.&amp;nbsp;Passing them in reverse order won&amp;#39;t trigger a compiler error since both are standard integer addresses, but it will map the signal routes backward, preventing the PPI channel from triggering.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;SPIM CS Line Ownership:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;The next thing I would check is whether the SPIM is still holding the CS pin. With a manual CS you have to set ss_pin = NRF_SPIM_PIN_NOT_CONNECTED, otherwise the driver keeps driving the pin and ends up fighting the GPIOTE task. Same goes for the spim node if they are using pinctrl.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;[quote user=""]. I realize that I will likely have to add a timer to meet the CS timing requirements of the ADC IC,[/quote]
&lt;p&gt;&lt;span&gt;Since the Timer output (&lt;/span&gt;&lt;code&gt;P1.12&lt;/code&gt;&lt;span&gt;) and DRDY (&lt;/span&gt;&lt;code&gt;P1.11&lt;/code&gt;&lt;span&gt;) both sit on the &lt;/span&gt;&lt;code&gt;GPIOTE20&lt;/code&gt;&lt;span&gt; instance, using &lt;/span&gt;&lt;code&gt;GPIOTE20&lt;/code&gt;&lt;span&gt; for the CS pin as well keeps the whole event-task loop inside a single hardware domain and will keep&amp;nbsp;&amp;nbsp;your resource allocation clean.&lt;/span&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span&gt;Please get back to us if you have more questions.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Best Regards&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Pallavi&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: GPIOTE trigger of SPIM read using EasyDMA with manual CS</title><link>https://devzone.nordicsemi.com/thread/570579?ContentTypeID=1</link><pubDate>Tue, 25 Aug 2026 11:54:46 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:7409de9f-515a-4528-9c4c-f099104d89cb</guid><dc:creator>Pallavi Patil</dc:creator><description>&lt;p&gt;Hie Jason,&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&amp;nbsp;We are currently looking into this.. I will update you as sonn as I have an answer from the team. Thanks for your patience and extremely sorry for the delay.&lt;/p&gt;
&lt;p&gt;Regards&lt;/p&gt;
&lt;p&gt;Pallavi&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>