This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

What's the maximum frequency for a Pin event on rising edge or falling edge ?

Hello everyone,

I generate an interruption when an rising edge is detected on one of my I/O.

It seems that the interruption doesn't trigger correctly for an input frequency higher than 40 kHz. In fact, when I have an input signal as an example of 70 kHz the frequency on which the interrupt is launch is blocking to 40 kHz.

is it due to the limitation of the system ? It seems to be incredible for a 64 Mhz clock ...

Best regards,

Sylvain.

#include <stdbool.h>
#include "nrf.h"
#include "nrf_drv_gpiote.h"
#include "app_error.h"
#include "boards.h"
#include "nrf_drv_timer.h"

#include "nrf_delay.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "app_util_platform.h"


#define PIN_IN 33 //BUTTON_1 

#define PIN_OUT BSP_LED_0

const nrf_drv_timer_t TIMER = NRF_DRV_TIMER_INSTANCE(1);
uint8_t state = 0, state_prev=0;
uint8_t ready = 0;
uint16_t captured_value,captured_prev = 0;
float frequency = 0.0;

void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
    nrf_drv_gpiote_out_toggle (34);
}

void timer_event_handler(nrf_timer_event_t event_type, void* p_context)
{
 //Never launch, do nothing
}

/**
 * @brief Function for configuring: input PIN, output PIN,
 * and configures GPIOTE to give an interrupt on pin change.
 */
static void gpio_init(void)
{
    ret_code_t err_code;

    err_code = nrf_drv_gpiote_init();
    APP_ERROR_CHECK(err_code);
    
    /* Configure output pin */
    nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(false);
    
    err_code = nrf_drv_gpiote_out_init(PIN_OUT, &out_config);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_gpiote_out_init(34, &out_config); //Output on P1.02 for debugging
    APP_ERROR_CHECK(err_code);
    /**************************************/

    /* Configure input for capture */
    nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
    in_config.pull = NRF_GPIO_PIN_PULLUP;

    err_code = nrf_drv_gpiote_in_init(PIN_IN, &in_config, in_pin_handler);
    APP_ERROR_CHECK(err_code);

    nrf_drv_gpiote_in_event_enable(PIN_IN, true);
    /**************************************/
}

static void timer_init (void){
    
    ret_code_t err_code;

    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    err_code = nrf_drv_timer_init (&TIMER, &timer_cfg, timer_event_handler);
    APP_ERROR_CHECK(err_code);
}

int main(void)
{
    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();

    NRF_LOG_INFO("HelloWorld");

    /*Macro for processing all log entries from the buffer.
    It blocks until all buffered entries are processed by the backend.*/
    NRF_LOG_FLUSH();
        
    timer_init();
    gpio_init();
    
    nrf_drv_timer_enable(&TIMER);
    
    while (true)
    {
        //Nothing to do as it's the first test
    }
}

/** @} */

  • Hello Sylvain,

    The code snippet that you sent looks like the start of the project, so I am not sure exactly what you intend to do with your interrupts.

    Do you plan to trigger a pin/LED, or do you want to perform a task at that rate? Doing extensive operations at 40kHZ could be quite difficult, but if you want to toggle a pin, or do some time measurements, that is quite doable.

    If you want to toggle pin 34 whenever you get a toggle on the input pin, I suggest that you look into the PPI module if you are looking at very high frequencies.

     

    Best regards,

    Edvin

  • Hi Edvin,

    My goal is to start the timer 1 on the rising edge of my input signal and stop it from the next rising edge of the same signal. In fact, my program works for a signal frequency below 40 kHz.

    So, to debug my program, I reduced it to its minimum. My idea was to toggle an output (For example p1.02 : 34) to follow my input signal.

    But I don't reach to follow my input signal if the frequency is above 40 kHz... and I need to capture a signal with a frequency of 100 kHz. Furthermore, I can't measure the signal during a long time... I really need to do it with only one period of my input signal.

    So, why the interruption can't follow the input signal ? I will be surprised that's due to the hardware limitation ...

    Sincerely yours,
    Sylvain

  • Ok. I understand.

     

    I would definitely use the PPI for this. 

    If you look at this case, I posted an example using the PPI. It does not trigger on GPIOTE inputs, but if you define the gpio to be the trigger, and use this to start an measure the timer, you will get something that can do this fast enough.

     

    The advantage using the PPI is that you do not need to use the CPU to start and stop the timer. It can be done by itself, without requiring the CPU.

    If you use the example I posted in the other thread, then you can set up an input something like this:

        NRF_GPIOTE->CONFIG[GPIO_CH_1] = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos | 
                                             GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos | 
                                             pin_number << GPIOTE_CONFIG_PSEL_Pos | 
                                             GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos;    // ignored when gpio is set to event mode
                                             
        NRF_GPIOTE->CONFIG[GPIO_CH_2] = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos | 
                                             GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos | 
                                             pin_number << GPIOTE_CONFIG_PSEL_Pos | 
                                             GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos;    // ignored when gpio is set to event mode

     

    Your Event End Points (EEP) and Task End Points (TEP) will look something like this:

    NRF_PPI->CH[PPI_CH_A].EEP = (uint32_t)&NRF_GPIOTE->EVENTS_IN[GPIO_CH_1];
    NRF_PPI->CH[PPI_CH_A].TEP = (uint32_t)&NRF_TIMER3->TASKS_START;
    
    NRF_PPI->CH[PPI_CH_B].EEP = (uint32_t)&NRF_GPIOTE->EVENTS_IN[GPIO_CH_2];
    NRF_PPI->CH[PPI_CH_B].TEP = (uint32_t)&NRF_TIMER3->TASKS_CAPTURE[3]; (value of timer will be stored in NRF_TIMER3->CC[3])
    NRF_PPI->FORK[PPI_CH_B].TEP = (uint32_t)&NRF_TIMER3->TASKS_STOP;
    NRF_PPI->FORK[PPI_CH_B].TEP = (uint32_t)&NRF_TIMER3->TASKS_CLEAR;

     

    As I mentioned in a comment, you can find the number of ticks in NRF_TIMER3->CC[3]; with the setup above. The FORK calls are there to stop the timer, and to reset the timer counter.

     

    Best regards,

    Edvin

  • I documented myself about PPI and I agree with you, this is the best solution.

    Before I start to implement my code, I'm asking myself about the way to do it because the same GPIOTE event (rising or falling edge, doesn't matter) may trigger two tasks (the stop and the start time task).

    I think, it can't work properly because the tasks are opposite each other . am I right ? So I don't see how to implement it in another way ...

    Furthermore, in your snippet code, you use multiple FORK for one channel but I think only one per channel is authorized ...

    My second question is relative to the configuration of the GPIOTE input. Since I'll use the PPI, I don't want that the GPIOTE event trigger an interrupt, but when I use the driver GPIOTE for the configuration it seems to always enable it. What's the way to tell to the driver that I don't want to trigger an interrupt?

        /*************************** Configure input for capture *****************************/
        nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
        in_config.pull = NRF_GPIO_PIN_PULLUP;
    
        err_code = nrf_drv_gpiote_in_init(PIN_IN, &in_config, in_pin_handler);
        APP_ERROR_CHECK(err_code);
        
        nrf_drv_gpiote_in_event_enable(PIN_IN, true);
        /*************************************************************************************/

    Kind regards,

    Sylvain.

  • You should differ between rising and falling (LoToHi and HiToLo) events if you want to use one to start the timer, and one to stop it.

     

     

    You are absolutely right about the forking. There is only one Fork per PPI channel. You must use another PPI channel if you want more tasks to trigger from the same event, but that is possible. Just have one event trigger two different PPI channels.

     

    You can set up the GPIOTE from the PPI configurations as well. Is there something specific that you aren't able to set up using the  NRF_GPIOTE->CONFIG[GPIO_CH_1] = ... setup from my previous reply? Note that you should use two GPIOTE events. One for rising (LoToHi) and one for falling (HiToLo).

     

    Best regards,

    Edvin

Related