<?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>Using timer capture, gpiote and ppi together to measure pulse widths</title><link>https://devzone.nordicsemi.com/f/nordic-q-a/122363/using-timer-capture-gpiote-and-ppi-together-to-measure-pulse-widths</link><description>Hi, 
 
 I am trying to set up a project that continuously measures the pulse width of a signal that may or may not be present at a pin. 
 I want to use a Timer in capture mode that automatically starts when the edge signal changes, and generates an interrupt</description><dc:language>en-US</dc:language><generator>Telligent Community 13</generator><lastBuildDate>Thu, 19 Jun 2025 11:52:06 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://devzone.nordicsemi.com/f/nordic-q-a/122363/using-timer-capture-gpiote-and-ppi-together-to-measure-pulse-widths" /><item><title>RE: Using timer capture, gpiote and ppi together to measure pulse widths</title><link>https://devzone.nordicsemi.com/thread/539874?ContentTypeID=1</link><pubDate>Thu, 19 Jun 2025 11:52:06 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:2884c24e-3761-4560-87d2-1f7c4dfe9f19</guid><dc:creator>Hung Bui</dc:creator><description>&lt;p&gt;Hi Billy,&amp;nbsp;&lt;br /&gt;My suggestion is to try breaking down the configuration and go step by step.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;You should try to verify:&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1 - You can trigger the GPIOTE event and get an interrupt&lt;/p&gt;
&lt;p&gt;2 - The GPIOTE event is connected to PPI and that trigger a capture task.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3 - Check if the CAPTURE task can generate the interrupt to store the value.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Using timer capture, gpiote and ppi together to measure pulse widths</title><link>https://devzone.nordicsemi.com/thread/539773?ContentTypeID=1</link><pubDate>Wed, 18 Jun 2025 17:46:24 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:4871f4ec-404f-49ff-aa92-8f11416e6278</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;Also in addition to my code above I wrote a handler for the SENT protocol, which is similar to IrDA in some ways; might be worth perusing.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://devzone.nordicsemi.com/f/nordic-q-a/118719/sent-protocol-based-sensor-configuration-with-nrf-boards"&gt;sent-protocol-based-sensor-configuration-with-nrf-boards&lt;/a&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Using timer capture, gpiote and ppi together to measure pulse widths</title><link>https://devzone.nordicsemi.com/thread/539772?ContentTypeID=1</link><pubDate>Wed, 18 Jun 2025 17:40:23 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:5bec94f5-4da0-477d-a689-d9742d8fc1f8</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;The use of interrupts works fine but may lead to issues if higher-priority interrupt mess about, typically with BLE and Softdevice enabled. There are several posts on the devzone discussing various pulsewidth measurements.&lt;/p&gt;
&lt;p&gt;Here is my test code which may or may not be useful; I use EGU in addition to PPI. This code is designed to simply run under the debugger where the results can be display by viewing the peripheral registers. Connect&amp;nbsp;PIN_PULSE_1 to&amp;nbsp;PIN_PULSE_2 to test without an external signal.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;// Summary: STOP eclipses both START and CLEAR; CLEAR eclipses START. This is a pain, since PPI could have
// otherwise used TEP to STOP and FORK to CLEAR. However there is a workaround by simply delaying the CLEAR
// by a single clock cycle at 16MHz by using the EGU without any firmware. This code performs CAPTURE,
// STOP and CLEAR with a single 16MHz clock cycle inserted between each step.
//
// The other thing to remember is that although Interrupts are level-sensitive and must be cleared each use,
// Events are edge sensitive and do not require clearing via TEP, FORK or indeed firmware. Here is a working
// solution which measures every PWM pulse; (a simpler solution could measure every other PWM pulse). I spoofed
// the PWM and didn&amp;#39;t bother adding the Capture[0] interrupt which would save the active-high PWM pulse-width
// somewhere. Tested using debugger, works fine. A single pin can be used as an input source for multiple
// channels of GPIOTE Events (though not for Tasks).
#define PIN_PULSE_1     13
#define PIN_PULSE_2     14
#define PPI_CHANNEL_A    0
#define PPI_CHANNEL_B    1
#define PPI_CHANNEL_C    2
#define PPI_CHANNEL_D    3
#define PPI_CHANNEL_E    4
#define GPIOTE_CHANNEL_A 0
#define GPIOTE_CHANNEL_B 1
#define GPIOTE_CHANNEL_C 2

// If the START task and the STOP task are triggered at the same time, meaning within the same period of PCLK16M, the STOP task will be prioritized
// STOP eclipses START and CLEAR
// CLEAR eclipses START
static void TestPPI_Pulsewidth(void)
{
    NRF_P0-&amp;gt;OUTCLR = (1 &amp;lt;&amp;lt; PIN_PULSE_1);
    NRF_P0-&amp;gt;OUTCLR = (1 &amp;lt;&amp;lt; PIN_PULSE_2);
    // Configuration                       Direction    Input            Pullup         Drive Level      Sense Level
    // ================================    ==========   ==============   ============   ==============   =============
    NRF_P0-&amp;gt;PIN_CNF[PIN_PULSE_1]        = (PIN_OUTPUT | PIN_CONNECT    | PIN_PULLNONE | PIN_DRIVE_H0H1 | PIN_SENSE_HIGH);
    NRF_P0-&amp;gt;PIN_CNF[PIN_PULSE_2]        = (PIN_INPUT  | PIN_CONNECT    | PIN_PULLNONE | PIN_DRIVE_H0H1 | PIN_SENSE_LOW);

    // Configure input for start on rising edge with capture on falling edge
    NRF_GPIOTE-&amp;gt;CONFIG[GPIOTE_CHANNEL_B] = GPIOTE_CONFIG_MODE_Event      &amp;lt;&amp;lt; GPIOTE_CONFIG_MODE_Pos     |
                                           GPIOTE_CONFIG_POLARITY_LoToHi &amp;lt;&amp;lt; GPIOTE_CONFIG_POLARITY_Pos |
                                           PIN_PULSE_2                   &amp;lt;&amp;lt; GPIOTE_CONFIG_PSEL_Pos     |
                                           GPIOTE_CONFIG_OUTINIT_Low     &amp;lt;&amp;lt; GPIOTE_CONFIG_OUTINIT_Pos;
    NRF_GPIOTE-&amp;gt;CONFIG[GPIOTE_CHANNEL_A] = GPIOTE_CONFIG_MODE_Event      &amp;lt;&amp;lt; GPIOTE_CONFIG_MODE_Pos     |
                                           GPIOTE_CONFIG_POLARITY_HiToLo &amp;lt;&amp;lt; GPIOTE_CONFIG_POLARITY_Pos |
                                           PIN_PULSE_2                   &amp;lt;&amp;lt; GPIOTE_CONFIG_PSEL_Pos     |
                                           GPIOTE_CONFIG_OUTINIT_Low     &amp;lt;&amp;lt; GPIOTE_CONFIG_OUTINIT_Pos;
    // Configure input for capture both rising and falling edges
    NRF_GPIOTE-&amp;gt;CONFIG[GPIOTE_CHANNEL_C] = GPIOTE_CONFIG_MODE_Event      &amp;lt;&amp;lt; GPIOTE_CONFIG_MODE_Pos     |
                                           GPIOTE_CONFIG_POLARITY_Toggle &amp;lt;&amp;lt; GPIOTE_CONFIG_POLARITY_Pos |
                                           PIN_PULSE_2                   &amp;lt;&amp;lt; GPIOTE_CONFIG_PSEL_Pos     |
                                           GPIOTE_CONFIG_OUTINIT_Low     &amp;lt;&amp;lt; GPIOTE_CONFIG_OUTINIT_Pos;

    // Start timer on rising edge of pulse via PPI
    NRF_PPI-&amp;gt;CH[PPI_CHANNEL_B].EEP = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;EVENTS_IN[GPIOTE_CHANNEL_B];
    NRF_PPI-&amp;gt;CH[PPI_CHANNEL_B].TEP = (uint32_t)&amp;amp;NRF_TIMER1-&amp;gt;TASKS_START;

    // Capture count and stop timer on falling edge via PPI
    NRF_PPI-&amp;gt;CH[PPI_CHANNEL_A].EEP = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;EVENTS_IN[GPIOTE_CHANNEL_A]; // Event
    NRF_PPI-&amp;gt;CH[PPI_CHANNEL_A].TEP = (uint32_t)&amp;amp;NRF_TIMER1-&amp;gt;TASKS_CAPTURE[0];            // Task #1
    NRF_PPI-&amp;gt;FORK[PPI_CHANNEL_A].TEP = (uint32_t)&amp;amp;NRF_EGU0-&amp;gt;TASKS_TRIGGER[0];            // Task #2

    NRF_PPI-&amp;gt;CH[PPI_CHANNEL_D].EEP = (uint32_t)&amp;amp;NRF_EGU0-&amp;gt;EVENTS_TRIGGERED[0];           // Event
    NRF_PPI-&amp;gt;CH[PPI_CHANNEL_D].TEP = (uint32_t)&amp;amp;NRF_TIMER1-&amp;gt;TASKS_STOP;                  // Task #1
    NRF_PPI-&amp;gt;FORK[PPI_CHANNEL_D].TEP = (uint32_t)&amp;amp;NRF_EGU0-&amp;gt;TASKS_TRIGGER[1];            // Task #2

    NRF_PPI-&amp;gt;CH[PPI_CHANNEL_E].EEP = (uint32_t)&amp;amp;NRF_EGU0-&amp;gt;EVENTS_TRIGGERED[1];   // Event
    NRF_PPI-&amp;gt;CH[PPI_CHANNEL_E].TEP = (uint32_t)&amp;amp;NRF_TIMER1-&amp;gt;TASKS_CLEAR;         // Task #1

    // Enable all 4 PPI channels
    NRF_GPIOTE-&amp;gt;EVENTS_PORT = 0;
    NRF_PPI-&amp;gt;CHENSET = (1UL &amp;lt;&amp;lt; PPI_CHANNEL_B) | (1UL &amp;lt;&amp;lt; PPI_CHANNEL_A) | (1UL &amp;lt;&amp;lt; PPI_CHANNEL_D) | (1UL &amp;lt;&amp;lt; PPI_CHANNEL_E);

    NRF_PPI-&amp;gt;CH[PPI_CHANNEL_C].EEP = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;EVENTS_IN[GPIOTE_CHANNEL_C]; // Event
    NRF_PPI-&amp;gt;CH[PPI_CHANNEL_C].TEP = (uint32_t)&amp;amp;NRF_TIMER1-&amp;gt;TASKS_CAPTURE[2];            // Task #1
    NRF_PPI-&amp;gt;CHENSET = (1UL &amp;lt;&amp;lt; PPI_CHANNEL_C);                                           // Enable PPI channel

    // Use Timer 1 in 32-bit timer mode at 16MHz
    NRF_TIMER1-&amp;gt;MODE      = TIMER_MODE_MODE_Timer&amp;lt;&amp;lt;TIMER_MODE_MODE_Pos; // Timer mode
    NRF_TIMER1-&amp;gt;BITMODE   = TIMER_BITMODE_BITMODE_32Bit&amp;lt;&amp;lt;TIMER_BITMODE_BITMODE_Pos; // 32 bits resolution
    NRF_TIMER1-&amp;gt;PRESCALER = 0UL; // Prescaler = 0
//  NRF_TIMER1-&amp;gt;SHORTS    = TIMER_SHORTS_COMPARE0_CLEAR_Enabled &amp;lt;&amp;lt; TIMER_SHORTS_COMPARE0_CLEAR_Pos
//                        | TIMER_SHORTS_COMPARE0_CLEAR_Enabled &amp;lt;&amp;lt; TIMER_SHORTS_COMPARE0_STOP_Pos;
    while (1)
    {
       // Spoof a PWM pulse on PIN_PULSE_1
       NRF_P0-&amp;gt;OUTSET = (1 &amp;lt;&amp;lt; PIN_PULSE_1);
       for (uint32_t i=0; i&amp;lt;1000000; i++) ;
       NRF_P0-&amp;gt;OUTCLR = (1 &amp;lt;&amp;lt; PIN_PULSE_1);
       NRF_TIMER1-&amp;gt;TASKS_CAPTURE[1] = 1;
    }
}
&lt;/pre&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>