Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Supervision timeout following connection parameter updates on limited devices

We are using SDK 15.3.0 and S140 v6.1.1 SoftDevice. We are using an external LF crystal with tolerance of +/-20ppm, and are therefore using

#define NRF_SDH_CLOCK_LF_SRC 1
#define NRF_SDH_CLOCK_LF_ACCURACY 7
which correspond to LF_SRC_XTAL and LF_ACCURAY_20PPM.
The nRF52840 is configured as a GAP peripheral and GATT server. It connects to iOS or Android devices.
In our application, we use two sets of connection parameters.
  • Set 1:
    • Min CI: 15ms
    • Max CI: 30ms
    • SL: 0
    • Supervision Timeout: 6s
  • Set 2:
    • Mic CI: 30ms
    • Max CI: 45ms
    • SL: 30
    • Supervision Timeout:6s

When first connecting, parameter set 1 is used. Set 2 is requested 30 seconds after ATT communication stops. For example, a phone connects to the nRF52, they exchange ATT commands for 10 seconds, then 30 seconds later, set 2 is requested.

We tested this setup on at least a dozen phones and a dozen nRF52s and it worked great. We even run a test for hours that switches between set 1 and set 2 at random intervals. This works great.

Recently, with new testers involved, they have found that 6 seconds (the supervision timeout) after set 2 is accepted (it seems to be after the params are accepted and not requested because both the phone and the nRF52 log use of the new parameters), a disconnection occurs due to a supervision timeout. While we know that interference could cause this, for certain individuals, this is 100% reproducible. They have tested in multiple physical locations (different interference profiles) and this can be reproduced on a variety of iOS and Android devices and a variety of nRF52 units. However, we have multiple individuals with the same model of phone, and only one person reproduces the issue.

We tried lowering the slave latency for set 2, and it eventually resolves the issue, but the value at which the issue is resolved varies case-by-case. Sometimes SL=10 fixes the disconnects. Sometimes it needs to be as low as 5.

I also tried a special nRF52 build with NRF_SDH_CLOCK_LF_ACCURACY set to 500ppm. I know this is recommended (required by the SDK asserts) if using the internal RC clock source, but we're not. The value of 500ppm did indeed resolve a consistent disconnect with SL=30.

Overall, it seems like we might be experiencing a clock tolerance issue, but I don't know how to prove this. We test in climate-controlled environments and our nRF52 board does not generate much heat. Unless the manufacturer is provided out 

We've also already released our nRF52 FW and we are hoping to only make changes to the phone app (we have a way for the phone to request the nRF52 to use new connection parameters)

In summary, we have a small set of phones and nRF52s that consistently disconnect after a connection parameter update with a CI/SL combo over a few hundred milliseconds. Most devices do not have an issue. This is resolved by lowering the CI/SL duration, but the duration is variable. We're curious whether there is any known issue with the connection parameter update procedure or RX window widening. Does this situation ring any obvious bells?

Parents Reply Children
  • I did try SYNTH for the clock source and it resolves the issue. However, I'd still like to characterize the issue with our XTAL to better understand the distribution of error across units.

    To do this, I created a small test for comparing the HF and LF clocks. I used the RTC, a Timer, and PPI. I set a RTC compare event to trigger a timer clear through PPI, and another RTC compare event to trigger a timer capture through PPI 10 seconds later.

    When using RC or XTAL for LF clock sources, I found the accuracy to be about 9000ppm (10090ms elapsed on timer vs 10s elapsed on RTC). This scaled if I increased or decreased the time, always being 9000ppm.

    To verify the test setup, I tried the SYNTH LF clock source. Over 10 seconds on the RTC, the timer read 9999.924ms (8ppm). I'd expect this to be closer to 0ppm, but I understand there is slight inaccuracies due to the different time domains.

    Are my PPM calculations incorrect, or is this a faulty test?

    #define COMPARE_COUNTERTIME  (10UL)
    
    nrf_ppi_channel_t ppiStartChannel;
    nrf_ppi_channel_t ppiStopChannel;
    const nrfx_timer_t timer = NRFX_TIMER_INSTANCE(4);
    const nrfx_rtc_t rtc = NRFX_RTC_INSTANCE(2); /**< Declaring an instance of nrf_drv_rtc for RTC0. */
    
    
    static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
    {
        if (int_type == NRF_DRV_RTC_INT_COMPARE0)
        {
            uint32_t capture_time = nrfx_timer_capture_get(&timer, (nrf_timer_cc_channel_t)0);
            NRF_LOG_DEBUG("RTC INT COMPARE PPI %d", capture_time)
            NRF_LOG_DEBUG("RTC INT COMPARE non-ppi %d", nrfx_timer_capture(&timer, (nrf_timer_cc_channel_t)0))
        }
        else if (int_type == NRF_DRV_RTC_INT_COMPARE1)
        {
            NRF_LOG_DEBUG("NRF_DRV_RTC_INT_COMPARE1")
            NRF_LOG_DEBUG("RTC INT COMPARE start %d", nrfx_timer_capture(&timer, (nrf_timer_cc_channel_t)0))
        }
    }
    
    /** @brief Function initialization and configuration of RTC driver instance.
     */
    
    void timer_event_handler(nrf_timer_event_t event_type,
                            void            * p_context)
    {
    
    }
    
    void rtc_config(void)
    {
        uint32_t err_code;
    
        // CONFIGURE RTC
        //
    
        //Initialize RTC instance
        nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG;
        config.prescaler = 4095;
        err_code = nrf_drv_rtc_init(&rtc, &config, rtc_handler);
        APP_ERROR_CHECK(err_code);
    
        //Set compare channel to trigger interrupt after COMPARE_COUNTERTIME seconds
        err_code = nrf_drv_rtc_cc_set(&rtc, 0, 2 + (COMPARE_COUNTERTIME * 8), true);
        APP_ERROR_CHECK(err_code);
    
        //Set compare channel to trigger interrupt after COMPARE_COUNTERTIME seconds
        err_code = nrf_drv_rtc_cc_set(&rtc, 1, 2, true);
        APP_ERROR_CHECK(err_code);
    
    
        // CONFIGURE TIMER
        //
        nrfx_timer_config_t timer_config =
        {
            .frequency = NRF_TIMER_FREQ_1MHz,
            .mode = NRF_TIMER_MODE_TIMER,
            .bit_width = NRF_TIMER_BIT_WIDTH_32,
            .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY,
            .p_context = 0
        };
        err_code = nrfx_timer_init(&timer, &timer_config, timer_event_handler);
        APP_ERROR_CHECK(err_code);
        nrfx_timer_clear(&timer);
        nrfx_timer_enable(&timer);
    
    
        // CONFIGURE PPI
        //
        err_code = nrfx_ppi_channel_alloc(&ppiStartChannel);
        APP_ERROR_CHECK(err_code);
        err_code = nrfx_ppi_channel_alloc(&ppiStopChannel);
        APP_ERROR_CHECK(err_code);
    
        const uint32_t rtc_compare1_event = nrfx_rtc_event_address_get(&rtc, NRF_RTC_EVENT_COMPARE_1);
        const uint32_t timer_start_task = nrfx_timer_task_address_get(&timer, NRF_TIMER_TASK_CLEAR);
        err_code = nrfx_ppi_channel_assign(ppiStartChannel, rtc_compare1_event, timer_start_task);
        APP_ERROR_CHECK(err_code);
    
        const uint32_t rtc_compare0_event = nrfx_rtc_event_address_get(&rtc, NRF_RTC_EVENT_COMPARE_0);
        const uint32_t timer_capture_task = nrfx_timer_capture_task_address_get(&timer, 0);
        err_code = nrfx_ppi_channel_assign(ppiStopChannel, rtc_compare0_event, timer_capture_task);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrfx_ppi_channel_enable(ppiStartChannel);
        APP_ERROR_CHECK(err_code);
        err_code = nrfx_ppi_channel_enable(ppiStopChannel);
        APP_ERROR_CHECK(err_code);
    
    
        // START RTC
        //
        nrf_drv_rtc_enable(&rtc);
        NRF_LOG_INFO("RTC started");
    }
    

  • Does Nordic have LF crystal part recommendations? We are using a low capacitance crystal that may have too stringent of a load capacitance range.

  • Hi

    I can't give you any crystal recommendation from specific vendors, but you can look at the bill of materials for reference designs and DKs to see what kind of crystals we have used there if that's helpful (downloadable here). The product specification should also explain what kind of crystals are fitting to use as LF crystals.

    Best regards,

    Simon

Related