Setting up the Timeslot API

Setting up the Timeslot API

When you want to use the radio while still maintaining a BLE connection or have a task that you need to do uninterrupted by radio activity, you can set up a timeslot. The timeslot is a period of time from 100 µs to 128 s in which the SoftDevice will render full control over the radio and other restricted peripherals to the user.

Required tools

  • Keil 5.20 or newer
  • nRF52 DK (alternatively nRF51 DK)
  • S132 SoftDevice (S130 for nRF51)
  • nRF5 SDK 11

Introduction

The application requests a timeslot, which can range from 100 µs to 100 ms. Longer timeslots can be granted by requesting extensions, up to a maximum of 128 s.

Timeslots are requested within a session, which can consist of multiple timeslots. Different types of sessions can be seen in the timeslot usage examples in Infocenter.

image description

When the timeslot is requested, the application needs to tell the SoftDevice to use either high or normal priority for the timeslot. For most applications normal priority should be used to avoid conflicts. If you have an application that is highly time critical, such as a proprietary radio protocol, use high priority.

There are two types of requests: earliest possible and given time. Earliest possible is for one-shot or reactive events and will give a timeslot at the earliest possible time. Given time is for recurring events and is granted an amount of time after the previous timeslot started.

When a timeslot has been granted the application will gain access to the RADIO, TIMER0, CCM, AAR, and PPI(ch 14-15) modules for the duration of the timeslot.

Please refer to chapter 11 of the S130/S132 SoftDevice specification for more indepth information.

Timeslot events and actions

The following are the timeslot functions:

  • sd_radio_session_open() - Open a session
  • sd_radio_session_close() - Close a session
  • sd_radio_request() - Request a timeslot

Timeslot events

In addition to these calls there is a signal handler that is called through the system event dispatcher, it contains these cases:

  • NRF_EVT_RADIO_SESSION_IDLE - The session has no remaining scheduled timeslots. If this event is triggered the application ends the session. Note that it is also possible to request new timeslots here(more on that later).

  • NRF_EVT_RADIO_SESSION_CLOSED - The session is closed and all acquired resources are released.

  • NRF_EVT_RADIO_BLOCKED - The requested timeslot could not be scheduled due to a collision with other activities. The application should request a new timeslot either at the earliest possible, or at the next normal position.

  • NRF_EVT_RADIO_CANCELED - The scheduled timeslot was cancelled by higher priority activity. The application should request a new timeslot.

  • NRF_EVT_RADIO_SIGNAL_CALLBACK_INVALID_RETURN - The last signal handler return value contained invalid parameters. Your application should assert.

Timeslot signals

The timeslot handler is called as an argument when opening a radio session. The timeslot handler will handle these cases:

  • NRF_RADIO_CALLBACK_SIGNAL_TYPE_START - The start of the timeslot. The application now has access to peripherals for the length of the timeslot. Before you start initializing your timeslot you must use a timer(for example TIMER0) that triggers an event 100 µs or so before the timeslot expires. TIMER0 is started automatically from zero by the SoftDevice at the start of the timeslot and is configured to run at 1 MHz. If you do not manage to do graceful shutdown by the end of the timeslot your application will hardfault.

  • NRF_RADIO_CALLBACK_SIGNAL_TYPE_RADIO - Radio interrupt, see the infocenter on the 2.4GHz radio for more information.

  • NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0 - Timer interrupt. This is where the interrupt from the graceful shutdown timer that you set up earlier ends up. Here you should deinitialize and prepare to return to SoftDevice activity. This is also where you can request the next timeslot.

  • NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEDED - The latest session extension succeeded. Normally no action is needed here.

  • NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED - The latest session extension failed, attempt to reschedule your timeslot.

The timeslot handler returns some callback parameters, which are used by the SoftDevice. Usage will be documented later.

  • NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE - Do nothing

  • NRF_RADIO_SIGNAL_CALLBACK_ACTION_END - Signals that the ongoing timeslot event is finished. The SoftDevice can resume its activities. Usually used to terminate an idle ongoing timeslot.

  • NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END - Signals that the ongoing timeslot event is finished, requests a new timeslot before signaling that the SoftDevice can resume its activities.

  • NRF_RADIO_SIGNAL_CALLBACK_ACTION_EXTEND - Attempt to extend an ongoing timeslot

The timeslot handler runs at the highest interrupt priority, 0, therefore you are not able to call functions that have lower priority.

Timeslot setup

Timeslot code is often quite long, the baseline is around 150 lines, however this can get significantly larger once you include functionality in your timeslot. This is why I prefer to add timeslots as a separate file in my project apart from main.c.

  1. Open the project you want to modify. In this example I started with ble_app_template.
  2. Right click the application folder and select Add New Item to Group 'Application'....

image description

  1. Select C File and name it "timeslot.c", click add.

Next we need to add some code to the file. The following code will request a session and toggle the state of LED4 on the start of each timeslot. We must also change the pin that is targeted by nrf_gpio_pin_toggle(20); to light up an LED on the nRF51/52 DK. For nRF51 use pin 24, for nRF52 use pin 20.

The configurations of the timeslots can be seen in the functions request_next_event_earliest(), configure_next_event_earliest() and configure_next_event_normal(). Here we set up timeslots that use normal priority and guaranteed crystal usage, which is needed for radio usage. Note that we set up a timer interrupt just before the end of the timeslot so that the application knows that it has to terminate its timeslot activity.

#include <stdint.h>
#include <stdbool.h>
#include "nrf.h"
#include "app_error.h"
#include "nrf_gpio.h"
#include "softdevice_handler.h"
#include "boards.h"

/**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;

/**@brief Request next timeslot event in earliest configuration
 */
uint32_t request_next_event_earliest(void)
{
    m_slot_length                                  = 15000;
    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  = 1000000;
    return sd_radio_request(&m_timeslot_request);
}


/**@brief Configure next timeslot event in earliest configuration
 */
void configure_next_event_earliest(void)
{
    m_slot_length                                  = 15000;
    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  = 1000000;
}


/**@brief Configure next timeslot event in normal configuration
 */
void configure_next_event_normal(void)
{
    m_slot_length                                 = 15000;
    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  = 100000;
    m_timeslot_request.params.normal.length_us    = m_slot_length;
}


/**@brief Timeslot signal handler
 */
void nrf_evt_signal_handler(uint32_t evt_id)
{
    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:
            err_code = request_next_event_earliest();
            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:
            //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;
            
            NRF_TIMER0->INTENSET = TIMER_INTENSET_COMPARE0_Msk;
            NRF_TIMER0->CC[0] = m_slot_length - 1000;
            NVIC_EnableIRQ(TIMER0_IRQn);   
            
            nrf_gpio_pin_toggle(20); //Toggle LED4
            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
            configure_next_event_normal();
            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;
}

Interfacing with main

Next we need to interface timeslot.c with our main file. Start by creating a header file.

  1. Right click the application folder and select Add New Item to Group 'Application'...
  2. Select Header File and name it "timeslot.h".
  3. The file now needs to be populated. Copy the following code into your file.
#ifndef TIMESLOT_H__
#define TIMESLOT_H__

#include 
#include 
#include "nrf.h"
#include "app_error.h"
#include "nrf_gpio.h"
#include "softdevice_handler.h"
#include "boards.h"

/**@brief Radio event handler
*/
void RADIO_timeslot_IRQHandler(void);


/**@brief Request next timeslot event in earliest configuration
 */
uint32_t request_next_event_earliest(void);


/**@brief Configure next timeslot event in earliest configuration
 */
void configure_next_event_earliest(void);


/**@brief Configure next timeslot event in normal configuration
 */
void configure_next_event_normal(void);
 
 
/**@brief Timeslot signal handler
 */
void nrf_evt_signal_handler(uint32_t evt_id);


/**@brief Timeslot event handler
 */
nrf_radio_signal_callback_return_param_t * radio_callback(uint8_t signal_type);


/**@brief Function for initializing the timeslot API.
 */
uint32_t timeslot_sd_init(void);


#endif

Next we need to add the path to our header file to the project.

  1. Go to Projects -> Options for Target, or click the shortcut shown below

image description

  1. Select the C/C++ tab and click ...

  2. In the window Folder Setup; add the path to "timeslot.h", for me this is ..\arm5_no_packs

image description


Lets configure our main.c file. We need to include the newly created header.

  1. Call #include "timeslot.h"

Add the signal handler to the system event dispatcher.

  1. Change the dispatcher from(NOTE: your starting point might look different):
static void sys_evt_dispatch(uint32_t sys_evt)
{
    pstorage_sys_event_handler(sys_evt);
    ble_advertising_on_sys_evt(sys_evt);
}

To:

static void sys_evt_dispatch(uint32_t sys_evt)
{
    pstorage_sys_event_handler(sys_evt);
    ble_advertising_on_sys_evt(sys_evt);
    nrf_evt_signal_handler(sys_evt);
}

We also need to initialize the timeslot in our main function

  1. Add timeslot_sd_init(); to main().

Now we are ready to compile and flash our project onto the development kit. Make sure that the kit is programmed with the S130/132 SoftDevice. You should see LED4 is blinking periodically, and that the device is advertising as Nordic_Template. Connecting and bonding to the device will not change the functionality of LED4.

Requesting an extension

An extension to the ongoing timeslot can be requested. This extension has to be larger than 200µs and cannot be cumulatively larger than 128 s. The following is an example of how to create an extension:

case NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0:
    //Timer interrupt - attempt to increase timeslot length
    signal_callback_return_param.params.extend.length_us = m_slot_length;
    signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_EXTEND;
    break;
case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED:
    //Extension succeeded, reset timer(configurations still valid since slot length is the same)
    NRF_TIMER0->TASKS_CLEAR = 1;
    break;
case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED:
    //Extension failed, schedule new timeslot at earliest time
    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;

Final notes and further reading

If you have any comments, feedback or questions, please feel free to leave them here, contact me by personal message or post a question on devzone. If you are having trouble with this tutorial it is better to post your question on devzone as this post is not actively monitored.