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
  • Hi,

     

    Calculation shows that the high speed timer is off by approx. 0.4%.

    By default, the HFCLK is sourced by the HFINT, which is will have a tolerance in the percentage area. Could you try to start the HFXO and see if this helps wrt. accuracy?

    Here's an example on how to request the HFXO using the clock_control driver:

    https://github.com/nrfconnect/sdk-nrf/blob/main/samples/bluetooth/direct_test_mode/src/dtm.c#L563-L593

     

    Kind regards,

    Håkon

  • 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

Reply Children
Related