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

Configuring nRF52840 for counting input frequency of upto 5MHz.

Hello,

I want to configure my nRF52840 development kit for counting input pulses on one of the GPIOs (Pin 13) in this case. I am using SEGGER Embedded Studio as IDE.

Here I am using PPI, GPIOTE and TIMER for this configuration. I have configured GPIOTE as an input event. As I want to count the rising edge on that particular GPIO, I have configured that GPIO to sense Low to High signal. According to this event, gpiote event handler is called and the static counter is incremented. 

As I searched in nordic forum, PPI is capable for operating in 8MHz. But when I apply 2MHz clock as input to the above mentioned GPIO, I don't get exact count in the watch window. As per the count, nRF kit is only able to count pulses at about 120kHz frequency.

If I apply clock below 120kHz it is counting perfectly. And when I increase the frequency, for example 1MHz or something, I get maximum count of 120kHz.

Is there anything missing here? Is my configuration correct? Is there a specific GPIO which has to be used for this purpose?

I want to capture the input frequency of about 5MHz. Is it possible with the nRF52840 dev kit?

Below is the configuration for PPI channel as well as timer0 settings.

static void gpiote_event_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
++m_gpiote_counter;
}

static void ppi_init(void)
{
uint32_t err_code = NRF_SUCCESS;
nrf_drv_gpiote_in_config_t config = NRFX_GPIOTE_RAW_CONFIG_IN_SENSE_LOTOHI(true);
config.pull = NRF_GPIO_PIN_NOPULL;
uint32_t gpiote_event_addr;
uint32_t PIN_NUMBER = 13;

err_code = nrf_drv_ppi_init();
APP_ERROR_CHECK(err_code);

err_code = nrf_drv_gpiote_init();
APP_ERROR_CHECK(err_code);
nrfx_gpiote_in_uninit(PIN_NUMBER);

err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel1);
APP_ERROR_CHECK(err_code);

err_code = nrf_drv_gpiote_in_init(PIN_NUMBER, &config, gpiote_event_handler);
APP_ERROR_CHECK(err_code);

nrf_drv_gpiote_in_event_enable(PIN_NUMBER, true);

gpiote_event_addr = nrf_drv_gpiote_in_event_addr_get(PIN_NUMBER);


err_code = nrf_drv_ppi_channel_assign(m_ppi_channel1,
gpiote_event_addr,
nrf_drv_timer_task_address_get(&m_timer0,
NRF_TIMER_TASK_COUNT));
APP_ERROR_CHECK(err_code);


// Enable configured PPI channels
err_code = nrf_drv_ppi_channel_enable(m_ppi_channel1);
APP_ERROR_CHECK(err_code);

}

static void timer0_init(void)
{
// Check TIMER0 configuration for details.
nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
timer_cfg.frequency = NRF_TIMER_FREQ_16MHz;
timer_cfg.mode = NRF_TIMER_MODE_COUNTER;
ret_code_t err_code = nrf_drv_timer_init(&m_timer0, &timer_cfg, timer0_event_handler);
APP_ERROR_CHECK(err_code);

nrf_drv_timer_extended_compare(&m_timer0,
NRF_TIMER_CC_CHANNEL0,
nrf_drv_timer_us_to_ticks(&m_timer0,
1),
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
true);
}

Thanks.

  • hmm, you're applying nrf_drv_timer_us_to_ticks() to a timer in counter mode... in fact, you'll get  a value 16 here, so interrupt will be called for every 16th pulse, is this what you expected?

    Instead of using interrupt, you can configure a timer without CLEAR shortcut and read counter value directly from timer with nrf_drv_timer_capture().

  • Thanks for your reply,

    No the requirement is not of getting interrupt at every 16th pulse. We want interrupt for every pulse so that counter can get increment and we get the frequency of the input signal.

    I tried you suggestion above. Also used nrf_drv_timer_capture() in the timer interrupt handler. However I am not getting timer interrupt. However I am getting GPIOTE interrupt here and its counter gets incremented. But as I stated previously, it can only count frequency up to 120kHz instead applying frequency of 3MHz on the gpio configured for GPIOTE.

    Below are the changes we made as per your comment:

    static void timer0_event_handler(nrf_timer_event_t event_type, void * p_context)
    {
        m_counter = nrf_drv_timer_capture(&m_timer0,NRF_TIMER_CC_CHANNEL0);
    }
    
    static void timer0_init(void)
    {
        // Check TIMER0 configuration for details.
        nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
        timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
        timer_cfg.mode = NRF_TIMER_MODE_LOW_POWER_COUNTER;
        ret_code_t err_code = nrf_drv_timer_init(&m_timer0, &timer_cfg, timer0_event_handler);
        APP_ERROR_CHECK(err_code);
    }
    
    

    Are the changes made fine?

    Why still I am not able to get timer interrupt? Is there any further configuration to be made?

    If possible, is there any demo for counting pulses of 3MHz on GPIO pin of nRF52840 dev kit from which we can take reference from?

    Thanks.

  • I didn't suppose you will get timer interrupt. Timer increments its value at GPIOTE event - why do you waste a lot of CPU cycles just to duplicate its work? When you need a counter value, call nrf_drv_timer_capture(), say, to channel 1 and then read CC[1].

    Concerning frequency limit, maybe the answer from this thread will help:

    "In the low power counter mode the hfclk is not requested until the COUNT task is trigerred, at that point the hfclk is requested for 1 clock clycle. So in practice this is a low power counter."

    So for each COUNT event, timer will start hfclk, wait until it get stabilized, increment counter, then stop hfclk. Try to call nrf_drv_clock_hfclk_request() at start, this will turn on hfclk forever (of course, power consumption will increase).

  • I suggest you try the workaround for Errata [155] GPIOTE: IN event may occur more than once on input edge. By keeping the clock on you might be able to capture more level shifts. 

    Do you have a scope of the input signal at different frequencies? I want to see if the signal is capacitively loaded. 

  • Thanks for your reply.

    Now we are able to count required frequency when we don't trigger an interrupt on every pulse. Thanks to your suggestions.

    However, for our end goal, we want to perform some task/logic on every rising edge of the pulse which we counted. Without using interrupt it doesn't seem to be achievable as due to interrupts, we are not able to count the input pulses accurately.

    Is there a way where we can achieve the end goal? Do we compulsorily need to use interrupt for performing some task when we detect rising edge or there is a way?

    Any logic or pseudo code or demo will be helpful here.

    Thanks.

Related