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

Latency of CTS CURRENT_TIME event after calling ble_cts_c_current_time_read()

I know that nrf_cal_set_time() from the nrf calendar example is NOT meant to be called on a regular basis / should only be called to update the time being kept locally to recalibrate after drift in the LfClk. But please humor me for a second -- I want to confirm that the reason this is problem is because of latency in getting the current time from a CTS central.

Notes on the following code:

  • the RTC is set to trigger a COMPARE0 event every 1 min
  • read_time() calls ble_cts_c_current_time_read()
  • the callback event from ble_cts_c_current_time_read() calls nrf_cal_set_time()
  • nrf_cal_set_time() updates the local time m_time (m_time was originally initialized to 0)
  • dostuff() prints out m_time. 

In summary, every 1 minute, this code in theory gets the time from the CTS and prints it out. 

void rtc_handler(nrf_drv_rtc_int_type_t int_type)
{
    if (int_type == NRF_DRV_RTC_INT_COMPARE0)
    {
        read_time(); // get the time
        
        dostuff();   // print the time

        //Increment CC so it'll trigger again in COMPARE_COUNTERTIME seconds
        rtc.p_reg->CC[0] += COMPARE_COUNTERTIME * 8;

        //Re-enable COMPARE0 event & interrupt
        nrf_drv_rtc_int_enable(&rtc, NRF_RTC_INT_COMPARE0_MASK);
    }
}

In reality, it seems like the time is being printed before it is retrieved from the CTS. The first time this runs (after 1 minute), dostuff() should print the time returned from CTS, but it instead prints 0, as if read_time() were never called. The next minute (minute 2), it prints the time from minute 1. 

My questions:

1. Is it right that this is happening because the latency in actually generating a CURRENT_TIME event? 

2. Is this why there is latency: After read_time() is called, control is returned to rtc_handler() to execute dostuff() just after the gatt read request is put on the SoftDevice queue. So this means that dostuff() is executed while the GATT read request might still be on the queue, and thus no time has yet been retrieved from CTS. 

3. I assume the queue is being processed by the softdevice in parallel with continuing to execute the application code? How many threads can run in parallel?

  • Hi,

    1. Most softdevice/BLE operations are asynchronous as the packets are only transmitted at the advertising/connection intervals. When you call ble_cts_c_current_time_read, it will schedule the transfer in the softdevice and you get an event back when the operation is completed.

    2. If read_time() only calls ble_cts_c_current_time_read() and does not wait for the event, this is the correct description. If you want to print the updated time, you need to block until the appropriate event (BLE_CTS_C_EVT_CURRENT_TIME) is received, before executing dostuff().

    3. Nothing runs in parallel. The softdevice uses TIMER/RTC to trigger interrupts in the future when it needs to take control back from the application. The Softdevice event will have higher interrupt priority than any part of the application, preempting any application task when it needs to handle timing-critical events like BLE operations. The softdevice knows when it can transmit the packets and will schedule operations in the future based on its own set of priorities.

    Best regards,
    Jørgen

  • Thank you so much. How do I block execution until the event comes back?

    you need to block until the appropriate event (BLE_CTS_C_EVT_CURRENT_TIME) is received
  • You can create a global flag that is set when the event handler when the event is received, something like this:

    static uint8_t cts_time_updated = false;
    
    on_cts_c_evt(...)
    {
    	...
    	case BLE_CTS_C_EVT_CURRENT_TIME:
    		cts_time_updated = true;
    	...
    }
    
    void rtc_handler(nrf_drv_rtc_int_type_t int_type)
    {
        if (int_type == NRF_DRV_RTC_INT_COMPARE0)
        {
            read_time(); // get the time
    		
    		while(!cts_time_updated);	//Wait for time to be set
    		cts_time_updated = false;	//Clear flag for next time
            
            dostuff();   // print the time
    
            //Increment CC so it'll trigger again in COMPARE_COUNTERTIME seconds
            rtc.p_reg->CC[0] += COMPARE_COUNTERTIME * 8;
    
            //Re-enable COMPARE0 event & interrupt
            nrf_drv_rtc_int_enable(&rtc, NRF_RTC_INT_COMPARE0_MASK);
        }
    }

    Note that you may also receive an invalid time, which will generate the event BLE_CTS_C_EVT_INVALID_TIME. You should handle this in a way in your application as well, to prevent a deadloop, for instance using a second flag that will exit the while loop.

Related