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

Connection event trigger and Timeslot API

Good day to all

I would like to get a little more information about the function:

// API for triggering a task when the SoftDevice is about to start a connection event 

sd_ble_gap_conn_evt_trigger_start();

added in softdevice version 7.0.1.

Starting the radio in connection mode is shown in the figure (taken from the online power calculator). At what point will this trigger be issued?

Using the timer that starts on this trigger, I want to use timeslots before the connection event. At the moment, I have received the following diagram:

general

For run trigger:

static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
    ret_code_t err_code;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
        {
            NRF_LOG_INFO("Connected.");
            err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
            APP_ERROR_CHECK(err_code);
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
            APP_ERROR_CHECK(err_code);

              
            ble_gap_conn_event_trigger_t trigger_params;
            trigger_params.ppi_ch_id = 0;
            trigger_params.task_endpoint = (uint32_t)&NRF_EGU3->TASKS_TRIGGER[0];
            trigger_params.conn_evt_counter_start = 1;
            trigger_params.period_in_events = 1;
            sd_ble_gap_conn_evt_trigger_start(m_conn_handle, &trigger_params);

        } break;
            ....
    }
    ...
}

IRQ Handlers:

// event's connection
void SWI3_EGU3_IRQHandler(void) 
{ 
  if (NRF_EGU3->EVENTS_TRIGGERED[0] != 0) 
  {
    NRF_EGU3->EVENTS_TRIGGERED[0] = 0;
    // Запускаем таймер на ожидание следующего интервала
    NRF_RTC2->TASKS_START = 1;

    // for debug with SALEAE: short pulse on gpio
    NRF_P1->OUTSET = 1 << 1;
    for(uint8_t i=0; i<63; ++i)
      __NOP();
    NRF_P1->OUTCLR = 1 << 1;
  }
}

void RTC2_IRQHandler (void)
{
  if(NRF_RTC2->EVENTS_COMPARE[0] == 1)
  {
    NRF_RTC2->EVENTS_COMPARE[0] = 0;
    __DSB();

    NRF_RTC2->TASKS_STOP = 1;
    NRF_RTC2->TASKS_CLEAR= 1;


    sd_power_mode_set(NRF_POWER_MODE_CONSTLAT);
    if(sd_clock_hfclk_request() != NRF_SUCCESS)
      return;

    NRF_P1->OUTSET = 1 << 2;
  }
}

Requsting timeslots:

static void ts_on_sys_evt(uint32_t sys_evt, void *p_context);
NRF_SDH_SOC_OBSERVER(timesync_soc_obs, TS_SOC_OBSERVER_PRIO, ts_on_sys_evt, 0);

void ts_on_sys_evt(uint32_t sys_evt, void *p_context) 
{
  switch (sys_evt) 
  {  
    case NRF_EVT_RADIO_BLOCKED:
    case NRF_EVT_RADIO_CANCELED: 
    case NRF_EVT_RADIO_SESSION_IDLE:
    case NRF_EVT_RADIO_SIGNAL_CALLBACK_INVALID_RETURN:
    { // CLOSE (!)
      uint32_t err_code = sd_radio_session_close();
      APP_ERROR_CHECK(err_code);
    }break;

  
  case NRF_EVT_RADIO_SESSION_CLOSED: 
    sd_clock_hfclk_release();
    sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
    NRF_P1->OUTCLR = 1 << 2;
    // release resourses (!)
  break;
  
  
  case NRF_EVT_HFCLKSTARTED:
    NRF_P1->OUTSET = 1 << 2;
    if(sd_radio_session_open(radio_callback) != NRF_SUCCESS)
      return;

    if(sd_radio_request(&m_timeslot_req_earliest) != NRF_SUCCESS) 
    {
      NRF_LOG_ERROR("sd_radio_request FAIL");
    }return;
  break;

    default: // No implementation needed.
//      NRF_LOG_INFO("Event: 0x%08x\r\n", sys_evt);
    break;
  }
}

// NOTE: This callback runs at lower-stack priority (the highest priority possible).
nrf_radio_signal_callback_return_param_t *radio_callback(uint8_t signal_type)
{
  switch (signal_type) 
  {
    case NRF_RADIO_CALLBACK_SIGNAL_TYPE_START:
    { // Таймслот запущен
      NRF_P1->OUTSET = 1 << 3;
      // TIMER0 is pre-configured for 1Mhz.
      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;
      NRF_TIMER0->EVENTS_COMPARE[1] = 0;

      if (m_send_sync_pkt) {
        NRF_TIMER0->INTENSET = (TIMER_INTENSET_COMPARE0_Set << TIMER_INTENSET_COMPARE0_Pos);
      } else {
        NRF_TIMER0->INTENSET = (TIMER_INTENSET_COMPARE0_Set << TIMER_INTENSET_COMPARE0_Pos) |
                               (TIMER_INTENSET_COMPARE1_Set << TIMER_INTENSET_COMPARE1_Pos);
      }
      NRF_TIMER0->CC[0]       = (TS_LEN_US - TS_SAFETY_MARGIN_US);
      NRF_TIMER0->CC[1]       = (TS_LEN_US - TS_EXTEND_MARGIN_US);
      NRF_TIMER0->BITMODE     = (TIMER_BITMODE_BITMODE_24Bit << TIMER_BITMODE_BITMODE_Pos);
      NRF_TIMER0->TASKS_START = 1;

      NRF_RADIO->POWER = (RADIO_POWER_POWER_Enabled << RADIO_POWER_POWER_Pos);

      NVIC_EnableIRQ(TIMER0_IRQn);
      m_total_timeslot_length = 0;
      timeslot_begin_handler();
    }break;

  case NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0:
  { // прерывание от таймера 0
    NRF_P1->OUTCLR = 1 << 3;
    if (NRF_TIMER0->EVENTS_COMPARE[0] &&
        (NRF_TIMER0->INTENSET & (TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENCLR_COMPARE0_Pos))) {
      NRF_TIMER0->TASKS_STOP        = 1;
      NRF_TIMER0->EVENTS_COMPARE[0] = 0;
      (void)NRF_TIMER0->EVENTS_COMPARE[0];

      // This is the "timeslot is about to end" timeout

      timeslot_end_handler();

      // Schedule next timeslot
      if (m_send_sync_pkt) {
        m_timeslot_req_normal.params.normal.distance_us = m_total_timeslot_length;
        return (nrf_radio_signal_callback_return_param_t *)&m_rsc_return_sched_next_normal;
      } else {
        return (nrf_radio_signal_callback_return_param_t *)&m_rsc_return_sched_next_earliest;
      }
    }

    if (NRF_TIMER0->EVENTS_COMPARE[1] &&
        (NRF_TIMER0->INTENSET & (TIMER_INTENSET_COMPARE1_Enabled << TIMER_INTENCLR_COMPARE1_Pos))) {
      NRF_TIMER0->EVENTS_COMPARE[1] = 0;
      (void)NRF_TIMER0->EVENTS_COMPARE[1];

      // This is the "try to extend timeslot" timeout

      if (m_total_timeslot_length < (128000000UL - 5000UL - TX_LEN_EXTENSION_US) && !m_send_sync_pkt) {
        // Request timeslot extension if total length does not exceed 128 seconds
        return (nrf_radio_signal_callback_return_param_t *)&m_rsc_extend;
      } else if (!m_send_sync_pkt) {
        // Don't do anything. Timeslot will end and new one requested upon the next timer0 compare.
      }
    }
  } break;

  case NRF_RADIO_CALLBACK_SIGNAL_TYPE_RADIO:
  { // прерывание радио
    RADIO_IRQHandler();
  } break;

  case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED:
  { // сбой при попытке расширить интервал
    // Don't do anything. Our timer will expire before timeslot ends
  } return (nrf_radio_signal_callback_return_param_t *)&m_rsc_return_no_action;

  case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED:
  {  // слот расширен успешно

    // Extension succeeded: update timer
    NRF_TIMER0->TASKS_STOP        = 1;
    NRF_TIMER0->EVENTS_COMPARE[0] = 0;
    NRF_TIMER0->EVENTS_COMPARE[1] = 0;
    NRF_TIMER0->CC[0] += (TX_LEN_EXTENSION_US - 25);
    NRF_TIMER0->CC[1] += (TX_LEN_EXTENSION_US - 25);
    NRF_TIMER0->TASKS_START = 1;

    // Keep track of total length
    m_total_timeslot_length += TX_LEN_EXTENSION_US;
  } break;

  default:
    break;
  };

  // Fall-through return: return with no action request
  return (nrf_radio_signal_callback_return_param_t *)&m_rsc_return_no_action;
}

I expected the session to be closed long before the start of the next connection event, but judging by the analyzer diagram, this happens later.

I have an assumption that this is due to handler priorities. The handler starts working later, after SD. Is this behavior correct?

I didn’t fully figure out how to correctly track the time of the time slot. Now I am measuring the time between events: NRF_RADIO_CALLBACK_SIGNAL_TYPE_START and NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0. 

Best regards,

Max

Related