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?