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

BLE interrupt priority question and BLE event lost issue

Hi,

I use nrf51822 + S130 platform, in my application I used ADC,UART and APP TIMER, I mens only these can cause interruptions,all these interrupt priority is 3(APP_IRQ_PRIORITY_LOWEST), The related code is as follows:

ADC init function in main.c:

/**
 * @brief ADC initialization.
 */
static void adc_init(void)
{
    ret_code_t ret_code;
    nrf_drv_adc_config_t config = NRF_DRV_ADC_DEFAULT_CONFIG;

    ret_code = nrf_drv_adc_init(&config, adc_event_handler);
    APP_ERROR_CHECK(ret_code);

    m_accelerator_channel_config.config.config.input = NRF_ADC_CONFIG_SCALING_INPUT_ONE_THIRD;
    m_break_channel_config.config.config.input = NRF_ADC_CONFIG_SCALING_INPUT_ONE_THIRD;
    m_accelerator_channel_child_config.config.config.input = NRF_ADC_CONFIG_SCALING_INPUT_ONE_THIRD;
    m_break_channel_child_config.config.config.input = NRF_ADC_CONFIG_SCALING_INPUT_ONE_THIRD;
    m_power_channel_config.config.config.input = NRF_ADC_CONFIG_SCALING_INPUT_ONE_THIRD;
    
    nrf_drv_adc_channel_enable(&m_accelerator_channel_config);
    nrf_drv_adc_channel_enable(&m_break_channel_config);
    nrf_drv_adc_channel_enable(&m_accelerator_channel_child_config);
    nrf_drv_adc_channel_enable(&m_break_channel_child_config);
    nrf_drv_adc_channel_enable(&m_power_channel_config);
    
    adc_start();
}

ADC event hadler in main.c:

/**
 * @brief ADC interrupt handler.
 */
static void adc_event_handler(nrf_drv_adc_evt_t const * p_event)
{
    if (p_event->type == NRF_DRV_ADC_EVT_DONE)
    {
        adc_start();
    }
}

ADC interrupt priority define in sdk_config.h:

#ifndef ADC_ENABLED
#define ADC_ENABLED 1
#endif
#if  ADC_ENABLED
// <o> ADC_CONFIG_IRQ_PRIORITY  - Interrupt priority
 

// <i> Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice
// <0=> 0 (highest) 
// <1=> 1 
// <2=> 2 
// <3=> 3 

#ifndef ADC_CONFIG_IRQ_PRIORITY
#define ADC_CONFIG_IRQ_PRIORITY 3
#endif

UART init function in main.c:

/**@brief Function for initializing the UART.
 */
static void uart_init(void)
{
    uint32_t err_code;

    app_fifo_init(&s_tUARTRxFIFO, s_ucUARTRxFIFOBuf, 128);
    
    const app_uart_comm_params_t comm_params =
      {
        .rx_pin_no    = RX_PIN_NUMBER,
        .tx_pin_no    = TX_PIN_NUMBER,
        .rts_pin_no   = RTS_PIN_NUMBER,
        .cts_pin_no   = CTS_PIN_NUMBER,
        .flow_control = APP_UART_FLOW_CONTROL_DISABLED,
        .use_parity   = false,
        .baud_rate    = UART_BAUDRATE_BAUDRATE_Baud115200
      };

    APP_UART_FIFO_INIT(&comm_params,
                        UART_RX_BUF_SIZE,
                        UART_TX_BUF_SIZE,
                        uart_event_handle,
                        APP_IRQ_PRIORITY_LOWEST,
                        err_code);
      
    APP_ERROR_CHECK(err_code);
}

UART event handler in main.c:

void uart_event_handle(app_uart_evt_t * p_event)
{
    uint8_t data;

    switch (p_event->evt_type)
    {
        /**@snippet [Handling data from UART] */
        case APP_UART_DATA_READY:      
            app_uart_get(&data);
            
            app_fifo_put(&s_tUARTRxFIFO, data);
    
            break;
        default:
            break;
    }
}

APP Timer init in main.c:

APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, NULL);

    app_timer_create(&system_timer_id, APP_TIMER_MODE_REPEATED, system_timer_handler);
    app_timer_start(system_timer_id, 32, NULL);

APP Timer event handler in main.c:

/** @brief system_timer_handler.
 */
void system_timer_handler(void * p_context)
{
    static uint8_t div_cnt = 0;
    uint32_t i;
    
    //parse data from COM and BLE every 5ms
    if (div_cnt++ == 5)
    {
        div_cnt = 0;
        
        MiniResponeDataParse();
        BLEPeripheralDataParse();
    }
    
    //process beep keep
    if (g_ulBeepKeepTime)
    {
        g_ulBeepKeepTime--;
        
        if (!g_ulBeepKeepTime)
        {
            nrf_gpio_pin_write(BEEP_PIN, BEEP_ACTIVE_STATE ? 0 : 1);
        }
    }
    
    //process counters
    for (i = 0; i < 16; i++)
    {
        if (g_tMainLoopInterface.pCounter[i])
        {
            if (*g_tMainLoopInterface.pCounter[i])
            {
                (*g_tMainLoopInterface.pCounter[i])--;
            }
        }
    }
}

APP Timer related interrupt priority define in app_timer.c:

#define RTC1_IRQ_PRI            APP_IRQ_PRIORITY_LOWEST                        /**< Priority of the RTC1 interrupt (used for checking for timeouts and executing timeout handlers). */
#define SWI_IRQ_PRI             APP_IRQ_PRIORITY_LOWEST                        /**< Priority of the SWI  interrupt (used for updating the timer list). */

APP_IRQ_PRIORITY_LOWEST define in app_util_platform.h:

#if __CORTEX_M == (0x00U)
#define _PRIO_SD_HIGH       0
#define _PRIO_APP_HIGH      1
#define _PRIO_APP_MID       1
#define _PRIO_SD_LOW        2
#define _PRIO_APP_LOW       3
#define _PRIO_APP_LOWEST    3
#define _PRIO_THREAD        4
#elif __CORTEX_M == (0x04U)
#define _PRIO_SD_HIGH       0
#define _PRIO_SD_MID        1
#define _PRIO_APP_HIGH      2
#define _PRIO_APP_MID       3
#define _PRIO_SD_LOW        4
#define _PRIO_SD_LOWEST     5
#define _PRIO_APP_LOW       6
#define _PRIO_APP_LOWEST    7
#define _PRIO_THREAD        15
#else
    #error "No platform defined"
#endif


//lint -save -e113 -e452
/**@brief The interrupt priorities available to the application while the SoftDevice is active. */
typedef enum
{
#ifndef SOFTDEVICE_PRESENT
    APP_IRQ_PRIORITY_HIGHEST = _PRIO_SD_HIGH,
#else
    APP_IRQ_PRIORITY_HIGHEST = _PRIO_APP_HIGH,
#endif
    APP_IRQ_PRIORITY_HIGH    = _PRIO_APP_HIGH,
#ifndef SOFTDEVICE_PRESENT
    APP_IRQ_PRIORITY_MID     = _PRIO_SD_LOW,
#else
    APP_IRQ_PRIORITY_MID     = _PRIO_APP_MID,
#endif
    APP_IRQ_PRIORITY_LOW     = _PRIO_APP_LOW,
    APP_IRQ_PRIORITY_LOWEST  = _PRIO_APP_LOWEST,
    APP_IRQ_PRIORITY_THREAD  = _PRIO_THREAD     /**< "Interrupt level" when running in Thread Mode. */
} app_irq_priority_t;
//lint -restore

So all my user interrupt priority is the lowest(3). I reference from here link text and here link text to know that the BLE Event interrupt priority is 2, is that right? So the ble_evt_dispatch function will not be affected by other user interrupts(ADC UART APP TIMER) because its interrupt priority is higher than they are,right?

Then, if what i said above is right, here's my problem.I use a variable named m_conn_tx_packet_count to record tx packet count, when connect be established I call sd_ble_tx_packet_count_get to get the total number of available guaranteed application transmission packets, when ble_nus_c_string_send return NRF_SUCCESS i decreasing m_conn_tx_packet_count,when receive BLE_EVT_TX_COMPLETE event i increasing m_conn_tx_packet_count,so i can call ble_nus_c_string_send only when there are free tx packets. The code is:

static void on_ble_central_evt(const ble_evt_t * const p_ble_evt)
{
    const ble_gap_evt_t   * const p_gap_evt = &p_ble_evt->evt.gap_evt;
    ret_code_t                    err_code;

    switch (p_ble_evt->header.evt_id)
    {
        /** Upon connection, check which peripheral has connected (HR or RSC), initiate DB
         *  discovery, update LEDs status and resume scanning if necessary. */
        case BLE_GAP_EVT_CONNECTED:
        {
            int i = 0, j = 0;
            
            NRF_LOG_INFO("Central Connected \r\n");
            /** If no nus is currently connected, try to find it on this peripheral*/
            
            for (i = 0; i < CENTRAL_LINK_COUNT; i++)
            {
                for (j = 0; j < 6; j++)
                {
                    if (p_gap_evt->params.connected.peer_addr.addr[j] != g_tMainLoopInterface.peripheral_info[i].mac[5 - j])
                    {
                        break;
                    }
                }
                
                if (j != 6)
                {
                    continue;
                }
                
                m_conn_handle_nus_c[i] = p_gap_evt->conn_handle;
                
                sd_ble_tx_packet_count_get(p_gap_evt->conn_handle, (uint8_t *)&m_conn_tx_packet_count[i]);
              
                break;
            }

        } break; // BLE_GAP_EVT_CONNECTED

        case BLE_EVT_TX_COMPLETE:
            for (uint8_t i = 0; i < CENTRAL_LINK_COUNT; i++)
            {
                if (p_gap_evt->conn_handle == m_conn_handle_nus_c[i])
                {
                    m_conn_tx_packet_count[i]++;
                    break;
                }
            }
            break;
        default:
            // No implementation needed.
            break;
    }
} 

My data send function code:

bool SendDatas(...)
{
    ....//other code

    if (m_conn_tx_packet_count[pars->peripheral_id] != 0)
    {
         if (ble_nus_c_string_send(p_ble_nus_c, &txbuf[send_index], factlen) == NRF_SUCCESS)
         {
              m_conn_tx_packet_count[pars->peripheral_id]--;
                        
              return true;
          }
   }

    return false;
}

Now the problem is, some times ble_nus_c_string_send never be called, because m_conn_tx_packet_count[pars->peripheral_id] always be 0. It seems like the last ble_nus_c_string_send call return NRF_SUCCESS but there's no BLE_EVT_TX_COMPLETE event received. Is it possible that the soft device lost BLE event? I've had similar problems before, and I have not solved them well,the link is herelink text . Both the two problem indicate that maybe some SYSTEM EVENT(BLE EVENT) have been lost. So I check all my interrupt config to make sure no interrupt's priority is higher than the priority of SOFTDEVICE_EVT_IRQ.

So, what wrong with my code? Was I not using the S130 correctly? Or is it the issue of S130?

Parents
  • I call SendDatas() in main loop every 10ms:

    int main(void)
    {
         init code
    
         for(;;)
         {
              if (timer told me 10ms is passed)
              {
                     SendDatas();
              }
    
              other code
         }
    }
    

    Actually it seems the problem is resolved, I test from yesterday to now the program work well, I take RK's advice and modify my code as this:

    The code when receive BLE_EVT_TX_COMPLETE event:

       case BLE_EVT_TX_COMPLETE:
                for (uint8_t i = 0; i < CENTRAL_LINK_COUNT; i++)
                {
                    if (p_gap_evt->conn_handle == m_conn_handle_nus_c[i])
                    {
                        CRITICAL_REGION_ENTER();
                        m_conn_tx_packet_count[i] += p_ble_evt->evt.common_evt.params.tx_complete.count;
                        CRITICAL_REGION_EXIT();
                        
                        break;
                    }
                }
                break;
    

    The SendDatas funciton:

     bool SendDatas(ptSendPar pars)
        {
              //some code
        
             while (there are datas to send)
             {
                  while (1)     //while loop until time out or BLE disconnect or data send succuss
                  {
                      while (m_conn_tx_packet_count[pars->peripheral_id] == 0)  //wait free tx packet 
                      {
                          if (timeout)
                          {
                              return false;
                          }
             
                         if (disconnect)
                         {
                              return false;
                         }
                      }
        
                     if (ble_nus_c_string_send(p_ble_nus_c, &txbuf[send_index], len) == NRF_SUCCESS)
                     {
                          CRITICAL_REGION_ENTER();
                          m_conn_tx_packet_count[pars->peripheral_id]--;
                          CRITICAL_REGION_EXIT();
                        
                          break;
                      }
        
                      the length of datas to send -= len
               }
        }
    

    The difference between the new code and the old code is:

    1、new code use m_conn_tx_packet_count[i] += p_ble_evt->evt.common_evt.params.tx_complete.count; and the old code use just m_conn_tx_packet_count[i] ++;

    2、new code use CRITICAL_REGION_ENTER and CRITICAL_REGION_EXIT then write m_conn_tx_packet_count

    I still do not understand why the old code will cause this problem(m_conn_tx_packet_count always be 0), even p_ble_evt->evt.common_evt.params.tx_complete.count is greater than 1 and i only increase m_conn_tx_packet_count by 1, it just will let the max value of m_conn_tx_packet_count less than it should be but will not make m_conn_tx_packet_count be 0.

    So is CRITICAL_REGION_ENTER and CRITICAL_REGION_EXIT solved my problem? I remember I used them before and there is still this problem, but I'm not so sure and I'm going to do more test about this.

    Anyone know what's the exactly issue in my old code? Any suggest will be helpful~

Reply
  • I call SendDatas() in main loop every 10ms:

    int main(void)
    {
         init code
    
         for(;;)
         {
              if (timer told me 10ms is passed)
              {
                     SendDatas();
              }
    
              other code
         }
    }
    

    Actually it seems the problem is resolved, I test from yesterday to now the program work well, I take RK's advice and modify my code as this:

    The code when receive BLE_EVT_TX_COMPLETE event:

       case BLE_EVT_TX_COMPLETE:
                for (uint8_t i = 0; i < CENTRAL_LINK_COUNT; i++)
                {
                    if (p_gap_evt->conn_handle == m_conn_handle_nus_c[i])
                    {
                        CRITICAL_REGION_ENTER();
                        m_conn_tx_packet_count[i] += p_ble_evt->evt.common_evt.params.tx_complete.count;
                        CRITICAL_REGION_EXIT();
                        
                        break;
                    }
                }
                break;
    

    The SendDatas funciton:

     bool SendDatas(ptSendPar pars)
        {
              //some code
        
             while (there are datas to send)
             {
                  while (1)     //while loop until time out or BLE disconnect or data send succuss
                  {
                      while (m_conn_tx_packet_count[pars->peripheral_id] == 0)  //wait free tx packet 
                      {
                          if (timeout)
                          {
                              return false;
                          }
             
                         if (disconnect)
                         {
                              return false;
                         }
                      }
        
                     if (ble_nus_c_string_send(p_ble_nus_c, &txbuf[send_index], len) == NRF_SUCCESS)
                     {
                          CRITICAL_REGION_ENTER();
                          m_conn_tx_packet_count[pars->peripheral_id]--;
                          CRITICAL_REGION_EXIT();
                        
                          break;
                      }
        
                      the length of datas to send -= len
               }
        }
    

    The difference between the new code and the old code is:

    1、new code use m_conn_tx_packet_count[i] += p_ble_evt->evt.common_evt.params.tx_complete.count; and the old code use just m_conn_tx_packet_count[i] ++;

    2、new code use CRITICAL_REGION_ENTER and CRITICAL_REGION_EXIT then write m_conn_tx_packet_count

    I still do not understand why the old code will cause this problem(m_conn_tx_packet_count always be 0), even p_ble_evt->evt.common_evt.params.tx_complete.count is greater than 1 and i only increase m_conn_tx_packet_count by 1, it just will let the max value of m_conn_tx_packet_count less than it should be but will not make m_conn_tx_packet_count be 0.

    So is CRITICAL_REGION_ENTER and CRITICAL_REGION_EXIT solved my problem? I remember I used them before and there is still this problem, but I'm not so sure and I'm going to do more test about this.

    Anyone know what's the exactly issue in my old code? Any suggest will be helpful~

Children
No Data
Related