<?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>nrf54l15 pwm capture on sdk v3.3.1</title><link>https://devzone.nordicsemi.com/f/nordic-q-a/128704/nrf54l15-pwm-capture-on-sdk-v3-3-1</link><description>I have a sensor that outputs PWM that I need to interface to. All I need is an input GPIO pin and using GPIOTE + GPPI + timer capture I would be able to determine the duty cycle ratio. 
 I found a sample program that gives me an idea on how to go about</description><dc:language>en-US</dc:language><generator>Telligent Community 13</generator><lastBuildDate>Tue, 14 Jul 2026 13:54:52 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://devzone.nordicsemi.com/f/nordic-q-a/128704/nrf54l15-pwm-capture-on-sdk-v3-3-1" /><item><title>RE: nrf54l15 pwm capture on sdk v3.3.1</title><link>https://devzone.nordicsemi.com/thread/569088?ContentTypeID=1</link><pubDate>Tue, 14 Jul 2026 13:54:52 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:0701c14f-5895-4427-ad77-c7cd1634c77d</guid><dc:creator>NordicUser0</dc:creator><description>&lt;p&gt;Hi&lt;/p&gt;
&lt;p&gt;Thanks for replying.&lt;/p&gt;
&lt;p&gt;So, if I understood you correctly if I keep&amp;nbsp;CONFIG_GPIO=y I should not call IRQ_CONNECT nor&amp;nbsp;nrfx_gpiote_init(). In the migration note it states:&lt;/p&gt;
&lt;p&gt;Action : Declare single driver instance within the system and share it among dependent software components, e.g. by using global scope.&lt;/p&gt;
&lt;p&gt;How do I access this driver interface?&lt;/p&gt;
&lt;p&gt;I did write code and if I set CONFIG_GPIO=n it works but if I set it to y it doesn&amp;#39;t:&lt;pre class="ui-code" data-mode="c_cpp"&gt;LOG_MODULE_REGISTER(CurrentSensor, LOG_LEVEL_DBG);

static nrfx_timer_t timer = NRFX_TIMER_INSTANCE(NRF_TIMER20);
static nrfx_gpiote_t gpiote =  NRFX_GPIOTE_INSTANCE(NRF_GPIOTE20);
static nrfx_gppi_t gppi;

void pwm_capture_handler(nrfx_gpiote_pin_t pin, nrfx_gpiote_trigger_t trigger, void *context) {
    pwm_capture_data_t *ch = (pwm_capture_data_t *)context;
    uint32_t ts = nrfx_timer_capture(&amp;amp;timer, ch-&amp;gt;cc_channel);
    int pin_high = nrf_gpio_pin_read(ch-&amp;gt;pin);

    if (ch-&amp;gt;index == 0 &amp;amp;&amp;amp; pin_high) {
        ch-&amp;gt;timestamps[0] = ts;
        ch-&amp;gt;index = 1;
    } else if (ch-&amp;gt;index == 1 &amp;amp;&amp;amp; !pin_high) {
        ch-&amp;gt;timestamps[1] = ts;
        ch-&amp;gt;index = 2;
    } else if (ch-&amp;gt;index == 2 &amp;amp;&amp;amp; pin_high) {
        ch-&amp;gt;timestamps[2] = ts;

        // Calculate raw values for this specific pulse
        uint32_t p = ch-&amp;gt;timestamps[2] - ch-&amp;gt;timestamps[0];
        uint32_t w = ch-&amp;gt;timestamps[1] - ch-&amp;gt;timestamps[0];

        // Add to accumulators (for averaging)
        ch-&amp;gt;acc_period += p;
        ch-&amp;gt;acc_pulse_width += w;
        ch-&amp;gt;sample_count++;

        // Reset for next cycle
        ch-&amp;gt;timestamps[0] = ts;
        ch-&amp;gt;index = 1;
    }
}

void pwm_capture_init_timer(void) {
    nrfx_timer_config_t config = NRFX_TIMER_DEFAULT_CONFIG(8000000);
    config.bit_width = NRF_TIMER_BIT_WIDTH_32;
    nrfx_timer_init(&amp;amp;timer, &amp;amp;config, NULL);
    nrfx_timer_enable(&amp;amp;timer);
}

void pwm_capture_init_gpiote(void)
{
   int err;
   //IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_GPIOTE20), IRQ_PRIO_LOWEST, nrfx_gpiote_irq_handler, &amp;amp;gpiote, 0);
#ifndef CONFIG_GPIO
   IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_GPIOTE20)+1, IRQ_PRIO_LOWEST, nrfx_gpiote_irq_handler, &amp;amp;gpiote, 0);
   err = nrfx_gpiote_init(&amp;amp;gpiote, NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY);
   if (err != 0) {
      LOG_ERR(&amp;quot;Failed to initialize GPIOTE driver: %x&amp;quot;, err);
   }
#endif
   //irq_enable(NRFX_IRQ_NUMBER_GET(NRF_GPIOTE20));
}
void pwm_gppi_init(void)
{
   nrfx_gppi_init(&amp;amp;gppi);
}


int pwm_capture_init_channel(pwm_capture_data_t *data, uint32_t pin, uint8_t cc_chan) {
    int err;
    uint8_t gpiote_ch;

    data-&amp;gt;pin = pin;
    data-&amp;gt;cc_channel = cc_chan;
    data-&amp;gt;index = 0;

    nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_NOPULL);

    // Allocate GPIOTE channel
    err = nrfx_gpiote_channel_alloc(&amp;amp;gpiote, &amp;amp;gpiote_ch);
    if (err != 0) {
        LOG_ERR(&amp;quot;Failed to allocate GPIOTE channel for pin %d. Err: %x&amp;quot;, pin, err);
        return -1;
    }

    // Configure Trigger
    nrfx_gpiote_trigger_config_t trig = {
        .trigger = NRFX_GPIOTE_TRIGGER_TOGGLE,
        .p_in_channel = &amp;amp;gpiote_ch
    };
    nrfx_gpiote_handler_config_t hand = {
        .handler = pwm_capture_handler,
        .p_context = data
    };
    nrfx_gpiote_input_pin_config_t cfg = {
        .p_trigger_config = &amp;amp;trig,
        .p_handler_config = &amp;amp;hand
    };

    err = nrfx_gpiote_input_configure(&amp;amp;gpiote, pin, &amp;amp;cfg);
    if (err != 0) {
        LOG_ERR(&amp;quot;GPIOTE config failed for pin %d&amp;quot;, pin);
        return -1;
    }

    nrfx_gpiote_trigger_enable(&amp;amp;gpiote, pin, true);

    // PPI Setup
    nrfx_gppi_handle_t gppi_h;
    err = nrfx_gppi_conn_alloc (
        nrfx_gpiote_in_event_address_get(&amp;amp;gpiote, pin),
        nrfx_timer_capture_task_address_get(&amp;amp;timer, cc_chan),
        &amp;amp;gppi_h);

    if (err != 0) {
        LOG_ERR(&amp;quot;PPI alloc failed for pin %d&amp;quot;, pin);
        return -1;
    }
    nrfx_gppi_conn_enable(gppi_h);
    LOG_INF(&amp;quot;PWM Capture Init Success: Pin %d on CC%d&amp;quot;, pin, cc_chan);
    return 0;
}

pwm_results_t pwm_capture_get_averages(pwm_capture_data_t *data) {
    pwm_results_t results = {0};

    // Lock interrupts to safely snapshot 64-bit values and reset
    unsigned int key = irq_lock();
    uint64_t total_p = data-&amp;gt;acc_period;
    uint64_t total_w = data-&amp;gt;acc_pulse_width;
    uint32_t count = data-&amp;gt;sample_count;

    // Reset accumulators for the next 1s window
    data-&amp;gt;acc_period = 0;
    data-&amp;gt;acc_pulse_width = 0;
    data-&amp;gt;sample_count = 0;
    irq_unlock(key);

    if (count &amp;gt; 0) {
        results.avg_period = (uint32_t)(total_p / count);
        results.avg_pulse_width = (uint32_t)(total_w / count);
    }

    return results;
}

void pwm_capture_log_debug(const char *label, pwm_results_t *res) {
    if (res-&amp;gt;avg_period == 0) {
        //LOG_WRN(&amp;quot;[%s] No pulses detected (Signal may be static 0%% or 100%%)&amp;quot;, label);
        return;
    }

    // Timer is at 8MHz -&amp;gt; 8,000,000 ticks per second
    double timer_freq = 8000000.0;

    double period_ms = (res-&amp;gt;avg_period / timer_freq) * 1000.0;
    double width_ms = (res-&amp;gt;avg_pulse_width / timer_freq) * 1000.0;
    double freq_hz = timer_freq / res-&amp;gt;avg_period;
    double duty_cycle = ((double)res-&amp;gt;avg_pulse_width / res-&amp;gt;avg_period) * 100.0;

    LOG_INF(&amp;quot;--- PWM Debug [%s] ---&amp;quot;, label);
    LOG_INF(&amp;quot;  Avg Period: %u ticks (%.3f ms)&amp;quot;, res-&amp;gt;avg_period, period_ms);
    LOG_INF(&amp;quot;  Avg Width:  %u ticks (%.3f ms)&amp;quot;, res-&amp;gt;avg_pulse_width, width_ms);
    LOG_INF(&amp;quot;  Frequency:  %.2f Hz&amp;quot;, freq_hz);
    LOG_INF(&amp;quot;  Duty Cycle: %.2f %%&amp;quot;, duty_cycle);
}

void pwm_capture_init(void)
{
   pwm_capture_init_timer();
   pwm_capture_init_gpiote();
}

&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;and main.c:&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/logging/log.h&amp;gt;
#include &amp;lt;hal/nrf_gpio.h&amp;gt;
#include &amp;lt;zephyr/drivers/sensor.h&amp;gt;

#include &amp;quot;currentSensor.h&amp;quot;

#define LOG_MODULE_NAME main
//LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_LOG_DEFAULT_LEVEL);
LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_DBG);

pwm_capture_data_t currentsensor;
pwm_results_t currentData;

int main(void)
{
   int dutyCycle = 0;
   pwm_capture_init();
   pwm_capture_init_channel(&amp;amp;currentsensor, NRF_GPIO_PIN_MAP(1, 9), NRF_TIMER_CC_CHANNEL3);
   while (1) {
      currentData = pwm_capture_get_averages(&amp;amp;currentsensor);
      if (currentData.avg_period != 0) {
         dutyCycle = (100 * currentData.avg_pulse_width) / currentData.avg_period;
      }
      LOG_DBG(&amp;quot;Current A: period: %d width: %d, duty: %d&amp;quot;, currentData.avg_period, currentData.avg_pulse_width, dutyCycle);
      k_msleep(1000);
   }
   return 0;
}
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Thanks&lt;/p&gt;
&lt;p&gt;Regards&lt;/p&gt;
&lt;p&gt;Sammy&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: nrf54l15 pwm capture on sdk v3.3.1</title><link>https://devzone.nordicsemi.com/thread/569054?ContentTypeID=1</link><pubDate>Mon, 13 Jul 2026 15:56:45 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:97a266bb-650b-4916-b700-583a8dacaba8</guid><dc:creator>Syed Maysum Abbas Zaidi</dc:creator><description>&lt;p&gt;Hi,&lt;/p&gt;
&lt;p&gt;That migration note is about how you register the GPIOTE interrupt handler in nrfx 4.0 when your application owns GPIOTE itself. But in your case it doesn&amp;#39;t apply. Since you&amp;#39;re keeping CONFIG_GPIO=y, the Zephyr GPIO driver already initializes GPIOTE and connects its interrupt at boot. So you don&amp;#39;t need NRFX_INSTANCE_IRQ_HANDLER_DEFINE.&lt;/p&gt;
&lt;p&gt;That note only becomes relevant if you run without the GPIO driver (CONFIG_GPIO=n).&lt;/p&gt;
&lt;p&gt;Best Regards,&lt;br /&gt;Syed Maysum&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: nrf54l15 pwm capture on sdk v3.3.1</title><link>https://devzone.nordicsemi.com/thread/569051?ContentTypeID=1</link><pubDate>Mon, 13 Jul 2026 15:35:07 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:10ed7858-38a1-4740-8941-923998742030</guid><dc:creator>NordicUser0</dc:creator><description>&lt;p&gt;From the nrfx 4.0 migration notes:&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;quot;Driver instance interrupt handler now has to be explicitly defined by user and call &lt;span class="tt"&gt;nrfx_gpiote_irq_handler&lt;/span&gt; with pointer to the driver instance as an argument. Removed &lt;span class="tt"&gt;NRFX_GPIOTE_INST_HANDLER_GET&lt;/span&gt; macro. &lt;br /&gt; Action : Use &lt;span class="tt"&gt;NRFX_INSTANCE_IRQ_HANDLER_DEFINE&lt;/span&gt;.&amp;quot;&lt;/p&gt;
&lt;p&gt;What does this actually mean? Are there any examples?&lt;/p&gt;
&lt;p&gt;Thanks&lt;/p&gt;
&lt;p&gt;Regards&lt;/p&gt;
&lt;p&gt;Sammy&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: nrf54l15 pwm capture on sdk v3.3.1</title><link>https://devzone.nordicsemi.com/thread/569050?ContentTypeID=1</link><pubDate>Mon, 13 Jul 2026 15:34:44 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:0364b418-1e56-41e9-9f90-c9a03cbb428f</guid><dc:creator>Syed Maysum Abbas Zaidi</dc:creator><description>&lt;p&gt;Hi,&lt;/p&gt;
&lt;p&gt;Yes you don&amp;#39;t need CONFIG_GPIO=n. You can keep the GPIO driver enabled and still do this. That sample disables GPIO&amp;nbsp;as it initializes GPIOTE and hooks the interrupt itself. Since you&amp;#39;re keeping CONFIG_GPIO=y, the GPIO driver already handles that at boot, so I think you just don&amp;#39;t need to copy the sample&amp;#39;s nrfx_gpiote_init() and IRQ_CONNECT() lines.&lt;/p&gt;
&lt;p&gt;For your sensor pin, set it up through nrfx, then read the capture values and work out the duty cycle in software. Keep using the normal GPIO API for all your other pins and just don&amp;#39;t mix both APIs on that one sensor pin.&lt;/p&gt;
&lt;p&gt;And there isn&amp;#39;t a PWM input sample for the nRF54L15, but if there is any confusion or if I have misunderstood your question please do let me know. Thanks&lt;/p&gt;
&lt;p&gt;Best Regards,&lt;br /&gt;Syed Maysum&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>