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

How to access the TIMER3 in wireless timer sync?

Hello,

I found this great blog entry about synchronizing timers on different devices: https://devzone.nordicsemi.com/nordic/short-range-guides/b/bluetooth-low-energy/posts/wireless-timer-synchronization-among-nrf5-devices

It works just fine on a nRF52840DK and a nRF52DK. Timers on multiple nRF5 devices are synced by one device (master) sending sync packets containing captured timer values to other devices (nodes). (TIMER3 is free running, TIMER2 counts TIMER3 overflows).

At the moment I am struggeling to access this synchronized TIMER3 on the two boards. I am familiar with setting up a TIMER without wireless timer sync. 

So here are my questions:

1. How can I define CC, Prescaler, Mode, Bitmode, the interrupt handler and so forth when using wireless timer sync?

2. Can anybody provide me some advice how to access this synchronized TIMER3 properly? 

Thank you very much in advance.

Parents
  • Hi Michael,

    So I am still having the abrupt offset issue.

    Do you have any other suggestions on how to solve this issue. We highly appreciate your help with our problem. It still looks like that the success of our project depends on your help.

    Thanks for the update. I have spent time in the office today to set up a better test case to reproduce the issue you see:

    On the receiver side I have a RTC set up to capture a timestamp every X ms, and I look at the relative difference between the current and previous timestamp to determine if this is a sane value. I think this is functionally equivalent of the setup you have?

    I believe I see the issue, and will work on confirming my setup and work on a solution.

    Best regards,

    Audun

Reply
  • Hi Michael,

    So I am still having the abrupt offset issue.

    Do you have any other suggestions on how to solve this issue. We highly appreciate your help with our problem. It still looks like that the success of our project depends on your help.

    Thanks for the update. I have spent time in the office today to set up a better test case to reproduce the issue you see:

    On the receiver side I have a RTC set up to capture a timestamp every X ms, and I look at the relative difference between the current and previous timestamp to determine if this is a sane value. I think this is functionally equivalent of the setup you have?

    I believe I see the issue, and will work on confirming my setup and work on a solution.

    Best regards,

    Audun

Children
  • Hello Audun,

    thank you very much for the reply and the effort you put into this.

    Basically this is a functionally equivalent of my setup. But one thing is different:

    On the receiver side of the sync beacons (peripheral) I have a TIMER (TIMER peripheral) to capture a timestamp every 40ms. Then I check the difference between the current and the previous timestamp, as you already mentioned. If the difference is higher than 41 or lower than 39 I generate an error.

    If you are interested, this is the configuration of the TIMER:

    static const nrfx_timer_t               m_timer = NRFX_TIMER_INSTANCE(1);
    
    void saadc_sampling_event_init(void)
    {
        ret_code_t err_code;
        err_code = nrf_drv_ppi_init();
        APP_ERROR_CHECK(err_code);
        
        nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG;
        timer_config.frequency = NRF_TIMER_FREQ_16MHz;
        err_code = nrfx_timer_init(&m_timer, &timer_config, timer_handler);
        APP_ERROR_CHECK(err_code);
    
        /* setup m_timer for compare event */
        uint32_t ticks = nrfx_timer_ms_to_ticks(&m_timer,1);
        nrfx_timer_extended_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
        nrfx_timer_enable(&m_timer);
    
        uint32_t timer_compare_event_addr = nrfx_timer_compare_event_address_get(&m_timer, NRF_TIMER_CC_CHANNEL0);
        uint32_t saadc_sample_event_addr = nrfx_saadc_sample_task_get();
    
        /* setup ppi channel so that timer compare event is triggering sample task in SAADC */
        err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
        APP_ERROR_CHECK(err_code);
        
        err_code = nrf_drv_ppi_channel_assign(m_ppi_channel, timer_compare_event_addr, saadc_sample_event_addr);
        APP_ERROR_CHECK(err_code);
    }    

    I setup a ppi channel so that timer compare event is triggering sample task in SAADC (every 1ms). In the saadc_callback I call my timestamp function every 40th time (every 40ms) .

    I believe I see the issue, and will work on confirming my setup and work on a solution.

    That sounds great. I am looking forward to hearing from you.

    Please let me know, if you need anything more.

    Thank you very much in advance.

    Best regards,

    Michael

  • Hi Michael,

    can you try to replace your timers_capture function again with the one below? This should have no restrictions on what interrupt level it can be called from. It won't have a potentially long delay either.

    With this change I haven't seen the issue again on my side. Will let my test case run over the weekend and see if any issues are triggered.

    I've not looked closely at the TIMER reference code you posted, but will take a closer look at that if the issue persists on your side.

    Best regards,

    Audun

    static void timers_capture(uint32_t * p_sync_timer_val, uint32_t * p_count_timer_val, uint32_t * p_peer_counter)
    {
        static nrf_atomic_flag_t m_timestamp_capture_flag = 0;
    
        nrf_atomic_u32_t peer_counter;
    
        if (nrf_atomic_flag_set_fetch(&m_timestamp_capture_flag) != 0)
        {
            // Not thread-safe
            APP_ERROR_CHECK_BOOL(false);
        }
    
        nrf_ppi_channel_t ppi_chn;
        nrfx_err_t ret = nrfx_ppi_channel_alloc(&ppi_chn);
        APP_ERROR_CHECK_BOOL(ret == NRFX_SUCCESS);
    
        ppi_counter_timer_capture_configure(ppi_chn);
    
        NVIC_DisableIRQ(m_params.egu_irq_type);
    
        bool counter_adjustment_triggered;
    
        // Loop while there is a chance of capturing during timer adjustment,
        // or if timer wraparound happens very close to the same clock cycle as the capture.
        do
        {
            counter_adjustment_triggered = m_params.egu->EVENTS_TRIGGERED[0];
    
            m_params.egu->TASKS_TRIGGER[1] = 1;
    
            if (counter_adjustment_triggered)
            {
                peer_counter = ((sync_pkt_t *) mp_curr_adj_pkt)->counter_val;
            }
            else
            {
                peer_counter = m_master_counter;
            }
        } while (counter_adjustment_triggered != m_params.egu->EVENTS_TRIGGERED[0] ||
                 m_params.high_freq_timer[0]->CC[3] < 2);
    
        NVIC_EnableIRQ(m_params.egu_irq_type);
    
        ppi_counter_timer_capture_disable(ppi_chn);
        nrfx_ppi_channel_free(ppi_chn);
    
        *p_sync_timer_val  = m_params.high_freq_timer[0]->CC[3];
        *p_count_timer_val = (m_params.high_freq_timer[1]->CC[0]);
        *p_peer_counter    = peer_counter;
    
        nrf_atomic_flag_clear(&m_timestamp_capture_flag);
    }

  • Hi again Michael,

    there were some corner cases that triggered during the weekend, and I've made some further refinements to the code now. Can you try the following on your side?

    static void timers_capture(uint32_t * p_sync_timer_val, uint32_t * p_count_timer_val, uint32_t * p_peer_counter)
    {
        static nrf_atomic_flag_t m_timestamp_capture_flag = 0;
    
        nrf_atomic_u32_t peer_counter;
    
        if (nrf_atomic_flag_set_fetch(&m_timestamp_capture_flag) != 0)
        {
            // Not thread-safe
            APP_ERROR_CHECK_BOOL(false);
        }
    
        nrf_ppi_channel_t ppi_chn;
        nrfx_err_t ret = nrfx_ppi_channel_alloc(&ppi_chn);
        APP_ERROR_CHECK_BOOL(ret == NRFX_SUCCESS);
    
        ppi_counter_timer_capture_configure(ppi_chn);
    
        NVIC_DisableIRQ(m_params.egu_irq_type);
    
        bool counter_adjustment_triggered;
    
        // Loop if adjustment procedure happened close to timer capture
        do
        {
            counter_adjustment_triggered = m_params.egu->EVENTS_TRIGGERED[0];
    
            m_params.egu->EVENTS_TRIGGERED[1] = 0;
            m_params.egu->TASKS_TRIGGER[1] = 1;
            while (m_params.egu->EVENTS_TRIGGERED[1] == 0)
            {
                __NOP();
            }
    
            if (m_params.high_freq_timer[1]->CC[0] < 2 || m_params.high_freq_timer[0]->CC[3] < 2)
            {
                /* Capture again if capture happened too close to adjustment or wraparound */
                m_params.egu->EVENTS_TRIGGERED[1] = 0;
                m_params.egu->TASKS_TRIGGER[1] = 1;
                while (m_params.egu->EVENTS_TRIGGERED[1] == 0)
                {
                    __NOP();
                }
            }
    
            if (counter_adjustment_triggered)
            {
                peer_counter = ((sync_pkt_t *) mp_curr_adj_pkt)->counter_val;
            }
            else
            {
                peer_counter = m_master_counter;
            }
        } while (counter_adjustment_triggered != m_params.egu->EVENTS_TRIGGERED[0] ||
                 m_params.high_freq_timer[0]->CC[3] < 2);
    
        NVIC_EnableIRQ(m_params.egu_irq_type);
    
        ppi_counter_timer_capture_disable(ppi_chn);
        nrfx_ppi_channel_free(ppi_chn);
    
        *p_sync_timer_val  = m_params.high_freq_timer[0]->CC[3];
        *p_count_timer_val = (m_params.high_freq_timer[1]->CC[0]);
        *p_peer_counter    = peer_counter;
    
        nrf_atomic_flag_clear(&m_timestamp_capture_flag);
    }

  • Hi again,

    I still see a corner case with this one.. rethinking the approach a bit now.

    Specifically I see corner cases around the time the timer wraps around (every 2.5 ms), or when the counter is cleared as part of the adjustment procedure on the receiver. The timer can just be sampled again when the corner case is adjusted and the new value captured, since this runs at 16 MHz and has no significant delay. One cannot wait for the counter in the same way, as this would incur a 2.5 ms delay. After some unsuccessful approaches to detect the counter corner case I'm trying to avoid clearing the counter altogether and keeping track of a diff instead of the transmitter counter total value. Will need to do some further testing to ensure no new issues have been introduced here.

    Best regards,

    Audun

  • Hi

    I've been following this case lately since I've been experiencing similar issues as Michael01101 after upgrading my project to the new TimeSync version. This case has been very helpful so far, thanks!

    I'm looking forward to a solution.

    Best regards,

    Jona

Related