NRFX Timer - incorrect elapsed time reading

Hi, 

I am using an nrf5340 with nrf connect sdk v1.9.1.
I need to calculate the duration of an event with nanosecond precision, so I am using an nrfx_timer at a frequency of 16MHz, so that I have a tick period of 62.5 ns.

Only for testing purposes, the event has a duration of milliseconds, but I realised that the duration calculated with the timer was always 5-8ms too early. 

To investigate further I wrote the following code, in which I trivially use a zephyr timer that periodically (every 2 seconds) reads and cleans the nrfx timer.

#define TIMER_TEST_PERIOD               2000000
#define NRFX_TIMER_PERIOD_AT_16MHz      62500

static const nrfx_timer_t timer  = NRFX_TIMER_INSTANCE(1);

void timer_test_exp() {
     uint64_t timer_count = nrfx_timer_capture(&timer, NRF_TIMER_CC_CHANNEL1);  
     uint64_t ns = timer_count * NRFX_TIMER_PERIOD_AT_16MHz / 1000;
     LOG_ERR("Timer count %d, %dns, err %d ns", timer_count, ns, TIMER_TEST_PERIOD*1000 - ns); 
     nrfx_timer_clear(&timer);
}

static void timer_handler(nrf_timer_event_t event_type, void *context)
{
	/*empty*/
}

void rb_timer_init()
{
    // Configure TIMER
    nrfx_timer_config_t timer_cfg = NRFX_TIMER_DEFAULT_CONFIG;
    timer_cfg.interrupt_priority = 0;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    err_code = nrfx_timer_init(&timer, &timer_cfg, timer_handler);
    if (err_code != NRFX_SUCCESS) {
        LOG_ERR("[rb_timer_init] nrfx_timer_init error: %08x", err_code);
        return;
    }
    
    k_timer_init(&timer_test, timer_test_exp, NULL);
    nrfx_timer_enable(&timer);
    k_timer_start(&timer_test, K_NO_WAIT, K_USEC(TIMER_TEST_PERIOD));
    LOG_WRN("[rb_timer_init] --- timer init ---");
}

With the following output: 

Again, I get an error of 6-7ms in advance. I would expect the nrfx timer to give me a value of 2'000'000'000 ns or something more, due to the delay of the k_timer handler call.
It seems really strange to me that the calculation comes out ahead of the period of the k_timer.

By calculating the duration using system ticks (k_cycle_get_32()), I paradoxically get closer to the actual duration. However, since the kernel has a frequency of 32kHz, I cannot go below 30.52 us per tick and for my purposes this is not sufficient.

I have tried various instances and channels of the nrfx_timer, with the same result.
How do I use the nrfx_timer accurately?

Regards,

APochiero

Parents Reply Children
  • Hi,

    By calling "clock_init", the error is lowered to about 300 microseconds.

    What is this behavior due to? How can I improve again?

    hw_interrupts: Timer count 31994967, 1999685437ns, errore 314563
    hw_interrupts: Timer count 31994972, 1999685750ns, errore 314250
    hw_interrupts: Timer count 31994954, 1999684625ns, errore 315375
    hw_interrupts: Timer count 31994977, 1999686062ns, errore 313938
    hw_interrupts: Timer count 31994967, 1999685437ns, errore 314563
    hw_interrupts: Timer count 31994980, 1999686250ns, errore 313750

    Kind regards,

    AP

  • You are testing two clock sources and putting them against each other. The LFCLK will introduce a delay of +/- 1 clock cycle (30.5 us), depending on when in the 32k clock period you schedule the new event.

    In addition, you're using the cpu to sample the NRF_TIMER instance, which also will give you a delay in terms of x amount of cycles.

    This goes through the kernel APIs before eventually going to your timer_test_exp() function. 

    Since you also log in the interrupt handler, before clearing the timer, you will get the added delay of the logging API as well. This can be deferred (ie. store a string to RAM), or in-place (ie. fire up the UART peripheral).

     

    If you want to run the NRF_TIMER instance every x amount of seconds, you should look into using the NRF_TIMER instance with DPPI/GPIOTE.

    Or setup the nrfx_timer with 2 seconds capture/compare, and toggle a GPIO in the interrupt and measure with a logic analyzer or oscilloscope to see how off it is.

     

    To use this for timing a specific function, you can do something like this:

    uint32_t pre_func = nrfx_timer_capture(&timer, NRF_TIMER_CC_CHANNEL1);
    my_function();
    uint32_t post_func = nrfx_timer_capture(&timer, NRF_TIMER_CC_CHANNEL1);
    # all timer variables must be unsigned 32 bit in order to handle overflow scenario, example: 1234-0xFFFFFFFF = 1235
    uint32_t total_time_in_ticks=post_func-pre_func;

     

    Kind regards,

    Håkon

  • Your suggestions have helped me a lot. Using DPPI and the external source, the timer is now very accurate.

    Thanks for the support

    AP

  • That is great to hear.

    Hope you have a wonderful weekend!

     

    Kind regards,

    Håkon

Related