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
    }
}

/** @} */

Parents
  • 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.

Reply
  • 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.

Children
  • 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

  • Hi Edvin,

    Working on two different type of edges can't work because of the duty cycle of the signal different that 50% ...

    That's seems complicate to do with PPI ... Maybe, even if it's ressource consuming, there could be a solution with two different timer. One of the timer could measure the high state and the other one could measure the low state. I'm reflecting about that ..

    Honestly, I don't test your code from your previous reply because I'm not familiar with this type of C (I'm coming from Atmel Studio) that's why I use the driver... Maybe there're resources to explain this type of code ?

    You tell me that I need to use two GPIOTE events but I was thinking that only one GPIOTE event can be assign per pin ?


    Kind regards,
    Sylvain.

  • Hello,

    It does not matter if the duty cycle is more or less than 50%. 

    Either, your PWM signal (pulse width modulation) is active low, or active high. I assume now that it is active high, but if it is not, just swap the rising and falling triggers/events.

     

    your signal:

                      --------------------_____________________--------------------_____________________

                      High                   Low                                      High                Low

                      <----------------->                                          <---------------------------------------------------->

                         Duty cycle                                                       Period

     

    Start the timer.

    on rising edge (LoToHi), clear the timer. The timer will still run, but it will be reset to start at 0.

    on falling edge, capture the time. this is how many ticks that the signal is high.

    on rising edge capture the time again (on a different capture register). and reset clear the timer again.

     

    This will give you two values. the time that the signal is high, and the time between two times the signal is high. That is your duty cycle and your period, respectively. It does not matter whether the duty cycle is more or less than 50% of the period.

    You can have several GPIOTE events for one pin. Unfortunately, there are no documentation on how to write the code to use these registers. But you have pretty much everything you need. 

    If you use an IDE, you can go to the definitions for the different commands, and see what options that you have.

    You can also look into the ppi example in the sdk. It uses a driver, but if you click through the functions in main, you will see how the driver is used.

    The example is located in SDK\examples\peripheral\ppi

     

     

    Best regards,

    Edvin

  • Edvin,

    Thank you for the support, you enlighted me on a lot of points.

    Sincerely,
    Sylvain

  • Glad I could help. Let me know if you encounter any more problems with the PPI.

     

    Best regards,

    Edvin

Related