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

Parents
  • Hello,

    during the wait, I made some progress in this matter, so the questions and additions below are considered more relevant.

    I didn’t notice that I uploaded the same image twice. According to this diagram:

    Q1. Does a trigger for a connection event occur at the time of the "radio start"?
    Q2. If I try to get timeslots up to this point, will I get a busy error because the stack is already busy with the radio?

    The description of the NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED has note:

    @note The SoftDevice will automatically turn on and off the external crystal,
    at the beginning and end of the timeslot, respectively. The crystal may also
    intentionally be left running after the timeslot, in cases where it is needed
    by the SoftDevice shortly after the end of the timeslot. 

    Q3. does this mean that I do not need to make a request: sd_clock_hfclk_request(); before starting sd_radio_session_open() ?

    Q4. About function: sd_ble_gap_conn_evt_trigger_start(conn_handle, params). Since I plan to use this event on the client side, I have to use it for every new connection. As a parameter, this function transfers the PPI channel number and the connection handle.

    Q4.1. It turns out that if there will be many connections, for example 10, then I need 10 PPI channels?

    Q4.2. Or is it possible to use only one channel for all any number of connections?

    Q5. I realized that in vain I was requesting an extension of the time slot and because of this the session time was stretching. After correction, the diagram began to look like this:

    Time between radio-session opened and timeslot started is 1.782553 ms. I correctly understood that this time includes the launch and stabilization of HFCLK(~ 1.5ms according to the first figure) and it will not work to reduce it?

    Q6. Running a time slot now looks like this:

    ble_gap_conn_evt_trigger launches RTC timer to measure the interval. The interval is defined as follows:

    RTC->CC = CONN_INTERVAL - TIMESLOT_LENGTH - 2*1500,

    where: CONN_INTERVAL - setted connection interval in microseconds. [e.g. 30000];

    TIMESLOT_LENGTH  - length of timeslot, [e.g 1000];

    1500 - total preparation time for launch (Preprossesing, Crystal Ramp-up, standby, ...).

    By this time I came empirically. This method works, but does not look reliable. Maybe I'm trying to invent a wheel? Is it possible to obtain time data more accurately without using magic numbers?

    Best regards,

    Max

Reply
  • Hello,

    during the wait, I made some progress in this matter, so the questions and additions below are considered more relevant.

    I didn’t notice that I uploaded the same image twice. According to this diagram:

    Q1. Does a trigger for a connection event occur at the time of the "radio start"?
    Q2. If I try to get timeslots up to this point, will I get a busy error because the stack is already busy with the radio?

    The description of the NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED has note:

    @note The SoftDevice will automatically turn on and off the external crystal,
    at the beginning and end of the timeslot, respectively. The crystal may also
    intentionally be left running after the timeslot, in cases where it is needed
    by the SoftDevice shortly after the end of the timeslot. 

    Q3. does this mean that I do not need to make a request: sd_clock_hfclk_request(); before starting sd_radio_session_open() ?

    Q4. About function: sd_ble_gap_conn_evt_trigger_start(conn_handle, params). Since I plan to use this event on the client side, I have to use it for every new connection. As a parameter, this function transfers the PPI channel number and the connection handle.

    Q4.1. It turns out that if there will be many connections, for example 10, then I need 10 PPI channels?

    Q4.2. Or is it possible to use only one channel for all any number of connections?

    Q5. I realized that in vain I was requesting an extension of the time slot and because of this the session time was stretching. After correction, the diagram began to look like this:

    Time between radio-session opened and timeslot started is 1.782553 ms. I correctly understood that this time includes the launch and stabilization of HFCLK(~ 1.5ms according to the first figure) and it will not work to reduce it?

    Q6. Running a time slot now looks like this:

    ble_gap_conn_evt_trigger launches RTC timer to measure the interval. The interval is defined as follows:

    RTC->CC = CONN_INTERVAL - TIMESLOT_LENGTH - 2*1500,

    where: CONN_INTERVAL - setted connection interval in microseconds. [e.g. 30000];

    TIMESLOT_LENGTH  - length of timeslot, [e.g 1000];

    1500 - total preparation time for launch (Preprossesing, Crystal Ramp-up, standby, ...).

    By this time I came empirically. This method works, but does not look reliable. Maybe I'm trying to invent a wheel? Is it possible to obtain time data more accurately without using magic numbers?

    Best regards,

    Max

Children
  •  Hi,

    Sorry for the late reply.

    I see that you have a couple questions that you want answered regarding time slots, and I'll try to answer the them :). But first I would like you to elaborate a bit more on this:

    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:

    From our developers:

    The event trigger task is triggered when the radio starts ramping up before the first reception or transmission in a connection event. That is, 40 us before the first radio activity. It is not possible to fit a timeslot in this period of time.
    What exactly is your goal of using radio timeslots? Did you want to use the Radio in the timeslot? If not, then maybe Radio notifications is the right approach. 
    The answer to Q3, and Q4 is yes :) 
    regards
    Jared
  • Hi,

    Sorry for the late reply.

    Do not worry about it, during this time I only plunged deeper into this topic)

    What exactly is your goal of using radio timeslots?

    We want use it for time synchronization (based on it). It is also necessary to ensure maximum energy efficiency of the device.

    Therefore, we decided to use the following scheme:

    time slots are activated before the event "connection interval".

    After a short exchange of tags, the radio switches to normal mode for data exchange already on the BLE channels.

    I have not yet come up with a better way to activate slots before this event occurs, as a countdown from the last interval.

    If not, then maybe Radio notifications is the right approach.

    I already read about it before, but why at the moment I could not remember. Thanks, I think it will take me a couple of days to comprehend the new information.

  • two days later ....


    Hello,

    I’ve got a little insight into the idea of radionifications, but so far I can’t say whether this tool suits me or not.

    I changed my code a bit to start the radio exchange through the time-slot api through the radio notification. Notifications were configured as follows:

    APP_ERROR_CHECK(radio_notification_init(5, NRF_RADIO_NOTIFICATION_TYPE_INT_ON_ACTIVE, NRF_RADIO_NOTIFICATION_DISTANCE_5500US));

    but I did not see anyone on the radio. For debugging, I assigned several pins to indicate certain events and made a recording with a logic analyzer.

    annotation:

    So, I see event from radio-notification 5500 us before the radio is activated (as planned).

    Next, I call the following functions:

    if(runtimeslot_)
    {   // event received
      runtimeslot_ = false;
      if(connected_)
      { // connect established
        err = sd_power_mode_set(NRF_POWER_MODE_CONSTLAT);
        if(err != NRF_SUCCESS)
          NRF_LOG_INFO("sd_power_mode_set %d", err);
    
        err = sd_clock_hfclk_request();
        if(err != NRF_SUCCESS)
          NRF_LOG_INFO("sd_clock_hfclk_request %d", err);
    
        NRF_P1->OUTSET = 1 << 2;
        err = sd_radio_session_open(radio_callback);
        if(err != NRF_SUCCESS)
          NRF_LOG_INFO("sd_radio_session_open %d", err);
    
        err = sd_radio_request(&m_timeslot_req_earliest);
        if(err != NRF_SUCCESS)
          NRF_LOG_INFO("sd_radio_request %d", err);;
      }
    }

    And timeslot is not activated, because in the event handler i get: NRF_EVT_RADIO_CANCELED.

    If i try reopened timeslot on it event, I see timeslot activity, but after connection event:

    So far, I'm at a loss. Is it possible to open a timeslot for transmitting proprietary data (ESB protocol) when using radio-notifications?

    how can I filter events? after all, everything falls under activity, including the “my” use of the radio.

    So far, my initial approach has more pluses, using only functions: sd_ble_gap_conn_evt_trigger_start();.

    Best regards.

    Max

  • additional information:

    I conducted a test and returned to starting time slots using the method described earlier (using a timer to start before the connection interval) without using NRF_RADIO_NOTIFICATION and got the following results

    As you can see in the figure marked 1, time slots open successfully (red track, p1.02).
    But if I just call a function

    APP_ERROR_CHECK(radio_notification_init(5, NRF_RADIO_NOTIFICATION_TYPE_INT_ON_ACTIVE, NRF_RADIO_NOTIFICATION_DISTANCE_5500US));
    whose handler does nothing but control the pin, then timeslots cease to be available.

Related