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

Need Help with Timeslot Extensions

I am currently trying to minimize radio noise when collecting data via the ADC, on a custom board powered by a battery (when the radio fires, it introduces a lot of noise into the captured values).

I have a capturing routine that runs for 10 seconds. I've made use of a radio notification event handler such that I will omit samples that are about to be affected by radio noise. This helps clean up my signal, but introduces holes into my captured data stream. To reduce these holes, I would like to use the Timeslot API to request a timeslot and extend it as much as possible during my reading routine. To my understanding, I can reduce the amount of link layer requests by having a longer maximum connection interval value, and by making use of the slave latency parameter to allow some of these connection intervals to be skipped.

My Timeslot code is as follows:

#include <stdint.h>
#include <stdbool.h>
#include "nrf.h"
#include "app_error.h"
#include "nrf_sdh.h"
#include "nrf_soc.h"
#include "boards.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

/**Constants for timeslot API
*/
static nrf_radio_request_t  m_timeslot_request;
static uint32_t             m_slot_length;

volatile uint32_t total_timeslot_length;
volatile uint32_t extend_success_count;
volatile uint32_t fail_count;

static nrf_radio_signal_callback_return_param_t signal_callback_return_param;

#define TIMESLOT_EXTEND_LENGTH 200//was 200
#define TIMESLOT_TIMER_INTERRUPT_END_MARGIN		50		//margin in us on timer 0 interrupt before the timeslot ends
#define QUIET_TIME 150000


/**@brief Request next timeslot event in earliest configuration
 */
uint32_t request_next_event_earliest(void)
{
	extension_request_flag = true;
    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, void * p_context)
{
	UNUSED_PARAMETER(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:
            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:
			NRF_TIMER0->INTENSET = TIMER_INTENSET_COMPARE0_Msk;
			NRF_TIMER0->BITMODE = (TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos);
			NRF_TIMER0->PRESCALER = (4 << TIMER_PRESCALER_PRESCALER_Pos);
            NRF_TIMER0->CC[0] = m_slot_length - TIMESLOT_TIMER_INTERRUPT_END_MARGIN;
            NVIC_EnableIRQ(TIMER0_IRQn);

			total_timeslot_length = m_slot_length;

			signal_callback_return_param.params.request.p_next = NULL;
            signal_callback_return_param.params.extend.length_us = TIMESLOT_EXTEND_LENGTH;
            signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_EXTEND;

            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:
			//timeslot is ending, signal the end action

        	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_END;
            break;
        case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED:

			extend_success_count++;

            if(total_timeslot_length >= QUIET_TIME)
			{
				signal_callback_return_param.params.request.p_next = NULL;
				signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE;
			}
			else
			{
				total_timeslot_length += TIMESLOT_EXTEND_LENGTH;

				NRF_TIMER0->CC[0] = total_timeslot_length - TIMESLOT_TIMER_INTERRUPT_END_MARGIN;
				signal_callback_return_param.params.request.p_next = NULL;
				signal_callback_return_param.params.extend.length_us = TIMESLOT_EXTEND_LENGTH;
				signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_EXTEND;
			}
            break;
        case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED:
            fail_count++;

			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_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;
}

before I start my routine, I call timeslot_sd_init(), and when the routine is over I call sd_radio_session_close();

My BLE configuration parameters are as follows:

#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_TIMEOUT_IN_SECONDS      0                                         /**< The advertising timeout (in units of seconds). */

#define MIN_CONN_INTERVAL               MSEC_TO_UNITS(7.5, UNIT_1_25_MS)             /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */
#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(30, UNIT_1_25_MS)             /**< Maximum acceptable connection interval (30 ms), Connection interval uses 1.25 ms units. */
#define SLAVE_LATENCY                   4                                           /**< 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 TX_POWER_LEVEL                  -16

My problem is as follows: it seems that only one timeslot gets requested, and it doesn't get extended past around 192000us. After calling my reading routine, extend_success_count will be roughly 21, and fail_count will be 1.

How can I maximize the duration of my timeslot, and continue to minimize the amount of radio noise during my routine? Should I be making use of request_next_event_earliest() during my routine somehow?

Parents
  • HI

    What is your ADC sampling frequency?

    Is there any way you could cover the missing samples by interpolating from surrounding samples?

    I don't know how much using timeslots will help compared to radio_notifications. In either case the Bluetooth stack will take precedence when a connection event is coming up (unless you are in slave latency sleep, and don't have any data in the buffers). Any chance you could increase the connection interval further? 
    It is possible to change the connection parameters dynamically, but issuing a connection parameter update request, but whether or not it is granted depends on the central device. 

    Are you saying that you are not able to get a new timeslot after the first one is unable to be extended?
    If you want to be in the timeslot as much as possible it is common practice to ask for the earliest possible timeslot after the extension fails. 

    Best regards
    Torbjørn Øvrebekk

     

     

  • I'm not able to get a new timeslot after the first one is unable to be extended yes.

    I've changed the extension request failed case to the following:

            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED:
                fail_count++;
    
                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_END;
                break;

    However I'm seeing the same extension performance results (just one extension, ending early).

    I don't believe I can change the connection interval without breaking iOS compatibility.

    ADC sampling frequency is once per millisecond, with every sixth millisecond saved into ram to be transferred over BLE later. I am employing an interpolation strategy, but I'd like to reduce the amount of times I have to interpolate by using timeslot extensions if possible.

  • Hi Mason

    What happens if you try to request a new timeslot as early as possible, using the NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END callback action?

    In theory that should give you a new timeslot as soon as there is some available time. 

    Best regards
    Torbjørn

  • NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED and NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END both evaluate to a value of 3, so I had to replace NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED with NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END  in order to compile the switch statement.

    In effect, absolutely nothing changes from before. The total timeslot length sits at about 19000us with roughly 21 successful extensions.

  • Hi Mason

    I am a bit unsure why your code isn't working, so I made a small example on my own to get timeslot extension working in a BLE example. 

    You can find the code here

    It will compile under SDK v15. Just copy the content of the zip into one of the example folders (for instance \nRF5_SDK_15.0.0_a53641a\examples\ble_peripheral\ble_app_uart_max_timeslot\)

    Could you have a look at this example and see if you can spot the difference?

    All the timeslot related code is available in max_timeslot.c/h. 

    Best regards
    Torbjørn

Reply
  • Hi Mason

    I am a bit unsure why your code isn't working, so I made a small example on my own to get timeslot extension working in a BLE example. 

    You can find the code here

    It will compile under SDK v15. Just copy the content of the zip into one of the example folders (for instance \nRF5_SDK_15.0.0_a53641a\examples\ble_peripheral\ble_app_uart_max_timeslot\)

    Could you have a look at this example and see if you can spot the difference?

    All the timeslot related code is available in max_timeslot.c/h. 

    Best regards
    Torbjørn

Children
No Data
Related