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

The SD doesn't grant me with the timeslot if i request the timeout of ~60ms or less. Larger timeouts work fine.

The SDK version is 15.0.0, SD 132, single peripheral conection.

the configuration:

#define APP_BLE_CONN_CFG_TAG            1                                           /**< A tag identifying the SoftDevice BLE configuration. */

#define DEVICE_NAME                     "GC"                               /**< Name of device. Will be included in the advertising data. */
#define NUS_SERVICE_UUID_TYPE           BLE_UUID_TYPE_VENDOR_BEGIN                  /**< UUID type for the Nordic UART Service (vendor specific). */

#define APP_BLE_OBSERVER_PRIO           3                                           /**< Application's BLE observer priority. You shouldn't need to modify this value. */

#define APP_ADV_INTERVAL                64                                          /**< The advertising interval (in units of 0.625 ms. This value corresponds to 40 ms). */

#define APP_ADV_DURATION                18000                                       /**< The advertising duration (180 seconds) in units of 10 milliseconds. */

#define MIN_CONN_INTERVAL               MSEC_TO_UNITS(20, UNIT_1_25_MS)             /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */
#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(75, UNIT_1_25_MS)             /**< Maximum acceptable connection interval (75 ms), Connection interval uses 1.25 ms units. */
#define SLAVE_LATENCY                   0                                           /**< Slave latency. */
#define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)             /**< Connection supervisory timeout (4 seconds), Supervision Timeout uses 10 ms units. */
#define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000)                       /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
#define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000)                      /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
#define MAX_CONN_PARAMS_UPDATE_COUNT    3                                           /**< Number of attempts before giving up the connection parameter negotiation. */

#define DEAD_BEEF                       0xDEADBEEF                                  /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */

#define UART_TX_BUF_SIZE                256                                         /**< UART TX buffer size. */
#define UART_RX_BUF_SIZE                256                                         /**< UART RX buffer size. */


#define SEC_PARAM_BOND                      1                                       /**< Perform bonding. */
#define SEC_PARAM_MITM                      0                                       /**< Man In The Middle protection not required. */
#define SEC_PARAM_LESC                      0                                       /**< LE Secure Connections not enabled. */
#define SEC_PARAM_KEYPRESS                  0                                       /**< Keypress notifications not enabled. */
#define SEC_PARAM_IO_CAPABILITIES           BLE_GAP_IO_CAPS_NONE                    /**< No I/O capabilities. */
#define SEC_PARAM_OOB                       0                                       /**< Out Of Band data not available. */
#define SEC_PARAM_MIN_KEY_SIZE              7                                       /**< Minimum encryption key size. */
#define SEC_PARAM_MAX_KEY_SIZE              16                                      /**< Maximum encryption key size. */

this is how i request the timeouts:

#define TIMESLOT_TIMEOUT 60000

/**@brief Request next timeslot event in earliest configuration
 */
uint32_t request_next_event_earliest(void)
{
    m_slot_length                                  = 1500;
    m_timeslot_request.request_type                = NRF_RADIO_REQ_TYPE_EARLIEST;
    m_timeslot_request.params.earliest.hfclk       = NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED;
    m_timeslot_request.params.earliest.priority    = NRF_RADIO_PRIORITY_NORMAL;
    m_timeslot_request.params.earliest.length_us   = m_slot_length;
    m_timeslot_request.params.earliest.timeout_us  = TIMESLOT_TIMEOUT;
    return sd_radio_request(&m_timeslot_request);
}


/**@brief Configure next timeslot event in earliest configuration
 */
void configure_next_event_earliest(void)
{
    m_slot_length                                  = 1500;
    m_timeslot_request.request_type                = NRF_RADIO_REQ_TYPE_EARLIEST;
    m_timeslot_request.params.earliest.hfclk       = NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED;
    m_timeslot_request.params.earliest.priority    = NRF_RADIO_PRIORITY_NORMAL;
    m_timeslot_request.params.earliest.length_us   = m_slot_length;
    m_timeslot_request.params.earliest.timeout_us  = TIMESLOT_TIMEOUT;
}


/**@brief Configure next timeslot event in normal configuration
 */
void configure_next_event_normal(void)
{
    m_slot_length                                 = 1500;
    m_timeslot_request.request_type               = NRF_RADIO_REQ_TYPE_NORMAL;
    m_timeslot_request.params.normal.hfclk        = NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED;
    m_timeslot_request.params.normal.priority     = NRF_RADIO_PRIORITY_HIGH;
    m_timeslot_request.params.normal.distance_us  = TIMESLOT_TIMEOUT;
    m_timeslot_request.params.normal.length_us    = m_slot_length;
}

With the timeouts of about 50ms and less, the application is not granted with the timeslot at all, no matter how short the timeslot length is requested.

My target is to get about 3ms long timeslots each 6ms, in a cycle. And of course i understand that from time to time there will be "BLOCKED" and "CANCELED" states.

Currently i am configuring the Timer0 like this: NRF_TIMER0->CC[0] = m_slot_length - 100. Is 100us enough for the SD to perform gracefull shutdown?

Please help.

Ivan.

  • Hi Ivan. 

    This is pretty strange. In our mesh SDK we use TIMESLOT_REQ_EARLIEST_TIMEOUT_US  = 15000 and it works fine for us. 

    Could you provide your source code/project that reproduce the issue so we can test here ?  You can make a minimum code to blink an LED on very timeslot. 

    Regarding the safety margin, as long as you are able to shut down the radio, 100us should be fine. 

  • Thank you, Hung Bui,

    Please find my project attached. It's based on the UART example of the SDK.nRF5_SDK_15.0.0_myTest.zip

  • Hi Sylvan, 

    Thanks for very nice minimal code. 

    I think I found the issue. Your nrf_evt_signal_handler() was not registered to receive system event. You would need to call NRF_SDH_SOC_OBSERVER() to register this handler. This is why when a radio timeslot is blocked , you won't have anything more.

    In your original code you request request_next_event_earliest() in the BLOCKED, CANCELLED event. But this may not what exactly you want as you don't want to have a few timeslot event right next to each other (earliest will give you next timeslot as soon as possible) but you may want to extend the distance and call configure_next_event_normal()  with larger distance when it's blocked.

    I attached here the the modified timeslot.c file that works for me. In the code I increased the distance 10ms each time it's blocked.

     

    #include <stdint.h>
    #include <stdbool.h>
    #include "nrf.h"
    #include "app_error.h"
    #include "nrf_gpio.h"
    #include "nrf_sdh.h"
    #include "boards.h"
    #include "nrf_sdh_soc.h"
    void nrf_evt_signal_handler(uint32_t evt_id,void * p_context);
    NRF_SDH_SOC_OBSERVER(m_sys_radio_signal_obs, 0, nrf_evt_signal_handler, NULL);
    /**Constants for timeslot API
    */
    static nrf_radio_request_t  m_timeslot_request;
    static uint32_t             m_slot_length;
    
    static nrf_radio_signal_callback_return_param_t signal_callback_return_param;
    
    #define TIMESLOT_TIMEOUT 40000
    static uint32_t distance_increased =0;
    /**@brief Request next timeslot event in earliest configuration
     */
    uint32_t request_next_event_earliest(void)
    {
        m_slot_length                                  = 3000;
        m_timeslot_request.request_type                = NRF_RADIO_REQ_TYPE_EARLIEST;
        m_timeslot_request.params.earliest.hfclk       = NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED;
        m_timeslot_request.params.earliest.priority    = NRF_RADIO_PRIORITY_HIGH;
        m_timeslot_request.params.earliest.length_us   = m_slot_length;
        m_timeslot_request.params.earliest.timeout_us  = TIMESLOT_TIMEOUT*10;
        return sd_radio_request(&m_timeslot_request);
    }
    
    
    /**@brief Configure next timeslot event in earliest configuration
     */
    void configure_next_event_earliest(void)
    {
        m_slot_length                                  = 3000;
        m_timeslot_request.request_type                = NRF_RADIO_REQ_TYPE_EARLIEST;
        m_timeslot_request.params.earliest.hfclk       = NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED;
        m_timeslot_request.params.earliest.priority    = NRF_RADIO_PRIORITY_HIGH;
        m_timeslot_request.params.earliest.length_us   = m_slot_length;
        m_timeslot_request.params.earliest.timeout_us  = TIMESLOT_TIMEOUT*10;
    }
    
    
    /**@brief Configure next timeslot event in normal configuration
     */
    void configure_next_event_normal(void)
    {
        m_slot_length                                 = 3000;
        m_timeslot_request.request_type               = NRF_RADIO_REQ_TYPE_NORMAL;
        m_timeslot_request.params.normal.hfclk        = NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED;
        m_timeslot_request.params.normal.priority     = NRF_RADIO_PRIORITY_HIGH;
        m_timeslot_request.params.normal.distance_us  = TIMESLOT_TIMEOUT;
        m_timeslot_request.params.normal.length_us    = m_slot_length;
    }
    uint32_t request_next_event_normal(uint32_t distance)
    {
    
        m_slot_length                                 = 3000;
        m_timeslot_request.request_type               = NRF_RADIO_REQ_TYPE_NORMAL;
        m_timeslot_request.params.normal.hfclk        = NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED;
        m_timeslot_request.params.normal.priority     = NRF_RADIO_PRIORITY_HIGH;
        m_timeslot_request.params.normal.distance_us  = distance;
        m_timeslot_request.params.normal.length_us    = m_slot_length;
        return sd_radio_request(&m_timeslot_request);
    }
    
    
    /**@brief Timeslot signal handler
     */
    void nrf_evt_signal_handler(uint32_t evt_id,void * p_context)
    {
        uint32_t err_code;
        
        switch (evt_id)
        {
            case NRF_EVT_RADIO_SIGNAL_CALLBACK_INVALID_RETURN:
                //No implementation needed
                break;
            case NRF_EVT_RADIO_SESSION_IDLE:
                //No implementation needed
                break;
            case NRF_EVT_RADIO_SESSION_CLOSED:
                //No implementation needed, session ended
                    break;
            case NRF_EVT_RADIO_BLOCKED:
                //Fall through
            case NRF_EVT_RADIO_CANCELED:
        
                distance_increased++;
                err_code = request_next_event_normal(TIMESLOT_TIMEOUT + distance_increased*10000);
                APP_ERROR_CHECK(err_code);
                break;
            default:
                break;
        }
    }
    
    
    /**@brief Timeslot event handler
     */
    nrf_radio_signal_callback_return_param_t * radio_callback(uint8_t signal_type)
    {
        switch(signal_type)
        {
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_START:
                NRF_TIMER0->TASKS_STOP          = 1;
                NRF_TIMER0->TASKS_CLEAR         = 1;
                NRF_TIMER0->MODE                = (TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos);
                NRF_TIMER0->EVENTS_COMPARE[0]   = 0;     
                
                //Start of the timeslot - set up timer interrupt
                signal_callback_return_param.params.request.p_next = NULL;
                signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE;
                distance_increased=0;
                NRF_TIMER0->INTENSET = TIMER_INTENSET_COMPARE0_Msk;
                NRF_TIMER0->CC[0] = m_slot_length - 500;
            	NRF_TIMER0->TASKS_START         = 1;
                NVIC_EnableIRQ(TIMER0_IRQn);   
                
    						nrf_gpio_pin_set (28);   
    						break;
    
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_RADIO:
                signal_callback_return_param.params.request.p_next = NULL;
                signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE;
                break;
    
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0:
                //Timer interrupt - do graceful shutdown - schedule next timeslot
    			nrf_gpio_pin_clear (28);   
                configure_next_event_normal();
                NRF_TIMER0->TASKS_STOP  = 1;
                NRF_TIMER0->EVENTS_COMPARE[0] = 0;
    			NRF_TIMER0->INTENCLR =TIMER_INTENSET_COMPARE2_Msk|TIMER_INTENSET_COMPARE1_Msk;
    
                signal_callback_return_param.params.request.p_next = &m_timeslot_request;
                signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END;
                break;
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED:
                //No implementation needed
                break;
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED:
                //Try scheduling a new timeslot
                configure_next_event_earliest();
                signal_callback_return_param.params.request.p_next = &m_timeslot_request;
                signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END;
                break;
            default:
                //No implementation needed
                break;
        }
        return (&signal_callback_return_param);
    }
    
    
    /**@brief Function for initializing the timeslot API.
     */
    uint32_t timeslot_sd_init(void)
    {
        uint32_t err_code;
        
        err_code = sd_radio_session_open(radio_callback);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
        
        err_code = request_next_event_earliest();
        if (err_code != NRF_SUCCESS)
        {
            (void)sd_radio_session_close();
            return err_code;
        }
        return NRF_SUCCESS;
    }
    
    

  • Thank you so much, Hung Bui. It works now.

    Using NRF_SDH_SOC_OBSERVER() is a kind of surprise for me 'cause my device is nothing but peripheral. Thanks anyway)) 

Related