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
  • Please, give me a answer on this questions about timeslots:

    If HFCLK always enable in my projects (I call sd_clock_hfclk_request() after stack init), does it mean that I can request an early timeslot interval  with NRF_RADIO_HFCLK_CFG_NO_GUARANTEE option without lost accuracy? In this case, I act as a guarantor that the exact clock source is already running.

  • Hi,

    So in short:

    • Radio notifications are used to inform the application of radio activity but will not give the application directly access to the radio peripheral.
    • Radio timeslots are used when you want access to the radio when the Softdevice is in use. The Softdevice will allocate a timeslot which gives the application directly access to the radio peripheral. 

    The issue with using radio timeslots  in this case is that a timeslot won't fit in between the event generated from sd_ble_gap_conn_evt_trigger_start() and the start of the connection interval. 

    CheMax said:
    If HFCLK always enable in my projects (I call sd_clock_hfclk_request() after stack init), does it mean that I can request an early timeslot interval  with NRF_RADIO_HFCLK_CFG_NO_GUARANTEE option without lost accuracy? In this case, I act as a guarantor that the exact clock source is already running.

     Yes, this will make sure that the Softdevice leave the HFCLK running and is ready when the timeslot starts. However, this may affect the possibility of getting an earlier timeslot as the Softdevice must start the HFCLK if it's not already running. 

    Do you have the option of synchronizing by using a GPIO, or does your project require that it's wireless?

    regards

    Jared

  • Hi Jared,

    Did not quite understand.


    If I started the clock in advance, then the soft device does not need to start and wait for the clock to stabilize.

    It is enough just to check the corresponding flag on the readiness of the source. Even the corresponding function is:

    /**@brief Checks if the high frequency crystal oscillator is running.
     *
     * @see sd_clock_hfclk_request
     * @see sd_clock_hfclk_release
     *
     * @param[out] p_is_running 1 if the external crystal oscillator is running, 0 if not.
     *
     * @retval ::NRF_SUCCESS
     */
    SVCALL(SD_CLOCK_HFCLK_IS_RUNNING, uint32_t, sd_clock_hfclk_is_running(uint32_t * p_is_running));


    If I started timing in advance, then I should not see the difference by requesting a slot with a NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED or with NRF_RADIO_HFCLK_CFG_NO_GUARANTEE.

    However, it is not. I saw a difference of about 1.5ms at startup.ms at startup.

      

    delay: 282.32us

    delay: 1.77 ms

    On figures:

    Yellow Label 1:

     

    if(sd_radio_session_open(radio_callback) != NRF_SUCCESS)
        return;
    
    if(sd_radio_request(&m_timeslot_req_earliest) != NRF_SUCCESS) 
        return;

    Yellow Label 2: After receive event: NRF_RADIO_CALLBACK_SIGNAL_TYPE_START.

    I can assume that a strict time delay is always used.


    Radio notifications are used to inform the application of radio activity but will not give the application directly access to the radio peripheral.

    I thought, based on the description, that if I ask you to tell me that activity will start in time X, then you can try to open the slot.

    and it seemed strange to me that when using the radio notification of activity, I can’t open the slot at these moments. I wrote about this above, if necessary, I can describe the full scheme of the experiment and attach the code and results.

    Do you have the option of synchronizing by using a GPIO, or does your project require that it's wireless?

    here I just want to refuse extra wires. At the moment, I get acceptable accuracy using the algorithm described (partially) here.

    Best regards,

    Max

  • Hi,

    Sorry for the misunderstanding in the previous reply. I was indeed thinking that you could use sd_radio_notification_cfg_set() to notify your application of a radio start event instead of using your previous method. I see that you tried this with the a 5 ms distance and that the Timeslot was canceled. What if you request the HFCLK before the radio notification and only open a timeslot in the radio notification event handler? The goal is to minimize time used in the event handler before you request a timeslot. 

    Regarding the difference between NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED and NRF_RADIO_HFCLK_CFG_NO_GUARANTEE. My guess is that the Softdevice is implemented such that it allocates time for the crystal to ramp up even though you've already enabled the HFCLK if you use NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED. 

  • Hi,

    What if you request the HFCLK before the radio notification and only open a timeslot in the radio notification event handler?

    No, it's not work.

    I noticed another feature. if I just allow radionotifications, then my chart is offset. Pay attention to the marker for the duration of the radio session (brown, channel 1). It is biased and overlaps the connection event (white, channel 0). I try all NRF_RADIO_NOTIFICATION_DISTANCES, but result but the result is always the same.

    without using this function, the picture is different.

    my suspicion is that allowing radioactivity tracking somehow blocks access to it programmatically. can you consult the developers of the stack about this behavior?

    The goal is to minimize time used in the event handler before you request a timeslot.

    Yes. And it seems that I have reached this minimum. I really wanted to be as close to the connection event as possible so as not to reduce the channel bandwidth. But this is apparently not solvable in the current performance.

    My guess is that the Softdevice is implemented such that it allocates time for the crystal to ramp up even though you've already enabled the HFCLK if you use NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED.

    truth can be found only from the author of the code :)

    if it is easier, then I can attach the project here. It is built on the basis of a standard example of a heart rate monitor (sdk 16.0.0). only the main.c file has been changed and added my file for working with time slots.

Reply
  • Hi,

    What if you request the HFCLK before the radio notification and only open a timeslot in the radio notification event handler?

    No, it's not work.

    I noticed another feature. if I just allow radionotifications, then my chart is offset. Pay attention to the marker for the duration of the radio session (brown, channel 1). It is biased and overlaps the connection event (white, channel 0). I try all NRF_RADIO_NOTIFICATION_DISTANCES, but result but the result is always the same.

    without using this function, the picture is different.

    my suspicion is that allowing radioactivity tracking somehow blocks access to it programmatically. can you consult the developers of the stack about this behavior?

    The goal is to minimize time used in the event handler before you request a timeslot.

    Yes. And it seems that I have reached this minimum. I really wanted to be as close to the connection event as possible so as not to reduce the channel bandwidth. But this is apparently not solvable in the current performance.

    My guess is that the Softdevice is implemented such that it allocates time for the crystal to ramp up even though you've already enabled the HFCLK if you use NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED.

    truth can be found only from the author of the code :)

    if it is easier, then I can attach the project here. It is built on the basis of a standard example of a heart rate monitor (sdk 16.0.0). only the main.c file has been changed and added my file for working with time slots.

Children
  • Hi,

    So I discussed this with the developers and they concluded that the Softdevice will not allow any timeslots right before a connection event, even by using Radio notifications. This is likely due to the timeslot overlapping with t_prep

    regards

    Jared

  • Hi,

    I'm afraid to seem annoying with this question, but I just want to better understand the tool that I use.

    Well, I understood about the overlap, But

    Let's see to this diagram,

    I always have free time to open the slot: t_free = t_ndist - t_prep. 

    Using my timer trigger method, I can successfully access the radio and exchange data. At the same time, it is enough for me 2 ms before the start of the connection event to activate the radio on 800 μs. This can be seen in the diagrams that I applied above. 

    This means that setting distance values of more than 2 ms should lead to successful activation of the slot without overlapping t_prep. However, this does not happen. 

  • Hi,

    Using Radio notifications with Timeslot is apparently not a feature which is supported by the radio scheduler in the Softdevice. This is a limitation of the Softdevice. It was my mistake of suggesting this solution when it's not a feature that we support in the first place. I'm sorry for that. Slight smile

    Regarding the "trigger method" of using sd_ble_gap_conn_evt_trigger_start() to signalize the start of a connection event. You mentioned that you're able to access the radio and exchange data with this method. I'm not sure how, as the trigger event will be generated 40 µs before a connection event, which leaves no room for a timeslot to fit. This method was explicitly not recommended from our developers of the Softdevice. 

    regards

    Jared

  • Hi, Ok.

    Thank you for your time.

    You mentioned that you're able to access the radio and exchange data with this method. I'm not sure how, as the trigger event will be generated 40 µs before a connection event, which leaves no room for a timeslot to fit.

    No, I use this trigger to start RTC. RTC will give an event 2 milliseconds before next connection event started. for one connection, by the way, this is the best solution).

    I suggest closing this topic as a topic with a solution found. I will mark key answers.

    Thanks again for your time.

Related