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?