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.

  • Hi Michael,

    I had some trouble replying to the most recent post, so I'm starting from the top again:

    Now I can achieve a current consumption of ~7mA @ 2.45 V. When I disable wireless timer sync by comment sync_timer_init() out, I have a consumption of ~1mA @ 2,45V. So it seems like the wireless timer sync cause an extra 6 mA. This sounds reasonable I guess?

    I would not expect the "ts_init()" function alone to cause ~1 mA power consumption. All activity (clock startup, constant latency power mode, radio RX) is started by "ts_enable()". If you want to measure a baseline current consumption value I recommend commenting out ts_enable() and also the advertising_start or scan_start functions (depending on BLE role). Without any of this activity, the current should be in the single-digit microamp range.

    We are still interested in lowering the total current consumption. We would appreciate very much if you could answer our previous questions:

    2. Can you please give me some suggestions what changes do I have to make?

    3. How can I implement that "the receivers only turn on the radio when it expects the transmitter to send a paket"?

    Thank you very much in advance.

    I've not done any work on this myself yet, but I have some ideas of what could be done.

    Ideally the receiver turns on the radio receiver right before it expects the transmitter to transmit a packet. If you use a fixed transmitter interval, this is not too complicated. There is some logic that would need to be added to the receiver, e.g. adding a "scanning " and "locked" mode, where the scanning mode runs the receiver continuously until it receives a packet. Once received, it would request the next radio timeslot for the next expected interval.

    There is one complicating factor: the most recent update to this code added a "TIME_SYNC_TX_OFFSET_REALIGN_TIMEOUT" option, where the transmitter actively adjusts the transmit timeslot to align with the timer wraparound (2.5 ms by default). This makes the transmission time more unpredictable. One can either disable this feature (set TIME_SYNC_TX_OFFSET_REALIGN_TIMEOUT to 0), or increase the receiver window to account for this jitter.

    A simpler approach would be to reduce the receiver duty cycle from e.g. 100% to 50% without actively trying to align with the transmitter. Lets say you increase the transmission rate from 1 Hz to 3 Hz, and reduce the scanning duty cycle from 100% to 50%, you are likely to receive just as many packets as before.

    In practice, both cases would involve changing the timeslot request type on the receiver side.

    Now the receiver always tries to extend the current timeslot: https://github.com/nordic-auko/nRF5-ble-timesync-demo/blob/master/nRF5_SDK_16.0.0_98a08e2/examples/common/time_sync.c#L394

    If the extension fails, it requests a new slot as soon as possible: https://github.com/nordic-auko/nRF5-ble-timesync-demo/blob/master/nRF5_SDK_16.0.0_98a08e2/examples/common/time_sync.c#L412

    Lets say you have a BLE connection interval of 100 ms. You could extend the timeslot for up to 50 ms total duration, and then request a "normal timeslot" for 100 ms later. This should avoid conflict with the BLE connection interval, and keep your scanning duty cycle at 50% (thereby reducing your power consumption by maybe 40%).

    Hope this helps!

    Audun

  • Hello Audun,

    thank you very much for your detailed answer. I really appreciate it.

    I will check your suggestions out next week!

    I have one additional question:

    If I disable the wireless timer sync for 80% of the time by using ts_disable() and enable wireless timer sync with  ts_enable() for 20% of the time, I could also lower the overall current consumption?

    At a 1Hz sync frequency I could enable wireless timer sync for 2 seconds and disable it for 8 seconds for example. So I could definetly receive a sync paket in this case.

    It looks like that ts_disable() does not stop the timers, so they will continue running I think. I could also comment out sd_clock_hfclk_release(). 

    When I enable wireless timer sync with ts_enable(), after I disabled it, but without calling

        timestamp_counter_start();
        sync_timer_start();

    the timers should not get resetted I think, but they will update themself when the next sync paket gets received. So the wireless timer sync functionality would work as it should. 

    Does this approach make sense to you? What do you think about it?

    Thank you very much in advance.

    Best regards,

    Michael

  • Hi Michael,

    Does this approach make sense to you? What do you think about it?

    It sounds like this is worth testing as a simple approach. As you say, if you don't call time timer _start() functions when you re-enable the receiver, the timers will keep running. I would also avoid calling the PPI config functions again when you re-enable, in case there could be corner cases associated with PPI configuration:

        ppi_timestamp_timer_configure();
        ppi_sync_timer_adjust_configure();

    Audun

  • Hello Audun,

    thank you very much for your reply.

    at the moment I am testing this simple approach with disabling and re-enabling the timer sync.

    I created the following function for disabling:

    uint32_t ts_temp_disable(void)
    {
        uint32_t err_code;
    
        nrf_atomic_flag_set(&m_pending_close);
    
        err_code = sd_radio_session_close();
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    
        // TODO: stop timer
    
        err_code |= sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    
        // TODO:
        //       - Close SoftDevice radio session (sd_radio_session_close())
        //       - Stop radio activity
        //       - Stop timers
        //       - Disable used PPI channels
        //       - Disable used interrupts
        //       - Release HFCLK (sd_clock_hfclk_release()),
        //       - Go back to low-power (mode sd_power_mode_set(NRF_POWER_MODE_LOWPWR))
        // Care must be taken to ensure clean stop. Order of tasks above should be reconsidered.
        return NRF_SUCCESS;
    }

    and the following function for re-enabling:

    uint32_t ts_re_enable(const ts_rf_config_t* p_rf_config)
    {
        uint32_t err_code;
    
        if (p_rf_config == NULL || p_rf_config->rf_addr == NULL)
        {
            return NRF_ERROR_INVALID_PARAM;
        }
    
        if (m_timeslot_session_open)
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        err_code |= sd_power_mode_set(NRF_POWER_MODE_CONSTLAT);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    
        memcpy(m_params.rf_addr, p_rf_config->rf_addr, sizeof(m_params.rf_addr));
        m_params.rf_chn = p_rf_config->rf_chn;
    
        err_code = sd_radio_session_open(radio_callback);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    
        err_code = sd_radio_request(&m_timeslot_req_earliest);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    
        NVIC_ClearPendingIRQ(m_params.egu_irq_type);
        NVIC_SetPriority(m_params.egu_irq_type, TIME_SYNC_EVT_HANDLER_IRQ_PRIORITY);
        NVIC_EnableIRQ(m_params.egu_irq_type);
    
        m_params.egu->INTENCLR = 0xFFFFFFFF;
        m_params.egu->INTENSET = EGU_INTENSET_TRIGGERED0_Msk | EGU_INTENSET_TRIGGERED2_Msk | EGU_INTENSET_TRIGGERED3_Msk | EGU_INTENSET_TRIGGERED4_Msk;
    
        m_blocked_cancelled_count  = 0;
        m_radio_state              = RADIO_STATE_IDLE;
    
        nrf_atomic_flag_clear(&m_send_sync_pkt);
    
        nrf_atomic_flag_set(&m_timeslot_session_open);
    
        return NRF_SUCCESS;
    }

    Of course, this functions are based on your enable and disable functions.

    It looks like my function for re-enabling works fine. But I have a problem with my disabling function. 

    I receive an error, after I call ts_temp_disable(). The error is produced in this line in time_sync.c:

    void SWI3_EGU3_IRQHandler(void)
    {
        ...
        if (NRF_EGU3->EVENTS_TRIGGERED[2] != 0)
        {
            NRF_EGU3->EVENTS_TRIGGERED[2] = 0;
            nrf_ppi_channel_disable(m_params.ppi_chns[4]);
    
            if (m_callback)
            {
                m_callback(&m_trigger_evt); // The error occurs here
            }
        }
        ...
    }

    The call stack looks like this: 

    Can you please provice me some advice how to solve this issue? In my optinion this approach is still promising for saving power.

    Thank you very much. Any kind of feedback is appreciated.

    Best regards,

    Michael

    -----------------------------------------------------------------------------------------------------------------------------

    Edit (2 hours later):

    Hello again,

    it looks like I fixed the problem by simply removing the line which causes the error (in time_sync.c):

    void SWI3_EGU3_IRQHandler(void)
    {    
        ...
        if (NRF_EGU3->EVENTS_TRIGGERED[2] != 0)
        {
            NRF_EGU3->EVENTS_TRIGGERED[2] = 0;
            nrf_ppi_channel_disable(m_params.ppi_chns[4]);
    
            if (m_callback)
            {
                //m_callback(&m_trigger_evt); // I commented this out
            }
        }
        ...
    }

    My ts_temp_disable() and my ts_re_enable(&rf_config) function work now. And the time sync works as it should when it is enabled.

    At the moment I am a bit worried by simply removing this line of code. To be honest, I do not really know what this line of code does. Does this have any consequences?

    Are there any side effects, which I sould be aware of?

    Thank you very much in advance. Any kind of feedback is appreciated.

    Best regards,

    Michael

  • Hi Michael, sorry for the late reply!

    My ts_temp_disable() and my ts_re_enable(&rf_config) function work now. And the time sync works as it should when it is enabled.

    At the moment I am a bit worried by simply removing this line of code. To be honest, I do not really know what this line of code does. Does this have any consequences?

    Are there any side effects, which I sould be aware of?

    I looked at the changes you made and the error you describe.

    I assume the error you see is actually coming from main.c. The callback you commented out (m_callback(&m_trigger_evt)) is probably going to this case in main.c: https://github.com/nordic-auko/nRF5-ble-timesync-demo/blob/master/nRF5_SDK_16.0.0_98a08e2/examples/ble_peripheral/ble_app_uart/main.c#L771 , which in turn calls ts_set_trigger(). This function will return an error when m_timeslot_session_open is false. This error is probably what triggers the assert.

    Considering that you only use the timestamp functionality, and not the ts_set_trigger() functionality, I think your changes should be fine. I think also that the trigger functionality isnt strictly speaking depending on that the timeslot being active, but this was probably the easiest way of checking if the required setup was done already. You could probably comment out this check and have the triggering too work fine when the radio is temporarily turned off to save power.

    Audun

Related