This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

HCI_EXT_HaltDuringRfCmd() with simultaneous Advertising and scanning problem

Other Parts Discussed in Thread: CC2540

Hi,

I tried to include HCI_EXT_HaltDuringRfCmd(HCI_EXT_HALT_DURING_RF_DISABLE); in my application init in my peripheral observer project. When I did that the program would stop running after a short time. LED's which are regulated by a PWM signal stopped shining, there's no advertising or scanning going on anymore, so I guess the system completely hung up.

So for test purposes I downloaded the peripheralObserver project anew and excluded any functionality but advertising and scanning and Timer 3 and 4 for the LED's to see when they stop shining so I know the system hung up.

It's the same with this one. If the device is only advertising or scanning theres no problem. As soon as I activate both, and HCI_EXT_HALT_DURING_RF_DISABLE is set the system hangs up after a short period. I also found out that if there's no advertises to scan, the device won't crash.

The controller is a CC2540. Adjusting scan window and interval only delay the system crash.

So is there a way to fix this and still use HCI_EXT_HALT_DURING_RF_DISABLE? Or is this setup working for somebody else?

Thanks in advance

Robin

  • Hello. How is the system hanging? Where is control when you pause the debugger? Is it still advertising?
  • Unfortunately I don't have debugging capabilities with my version of IAR. It is not advertising anymore nor entering my periodic task in which I vary the external LEDs. The LEDs always shut down completely when it hangs up, though the program never forces them to completely go out once the timers for the pwm are started. I've tried it quite a few times now and the LEDs never kept shining when it hangs up.
  • Atleast that's what i can see with the sniffer and from the LEDs.

    Edit:

    I tried it now to don't regulate the LEDs with a pwm but the pin set as GPIO-output set high and the lights are still going out if it hangs up.

  • This is strange since, if anything, disabling HALT_DURING_RF should open up more processing capability.

    What stack are you using? I can try to set something similar up to look at.
  • I use the latest 1.4.1.43908b

    I can also send you the testproject I created if that helps. I only had to modify the peripheralObserverProfile a little bit to get it working at all (without disabling HALT_DURING_RF).

  • Hello. Sure, you can send out the modified files. I'll take a look.
  • Okay so here's my test-project folder and the profile-role.

    I hope it works with the file attached this way.

    At line 367 of simpleBLEPeripheralObserver the "HCI_EXT_HaltDuringRfCmd(HCI_EXT_HALT_DURING_RF_DISABLE);" is currently commented out.

    thanks for taking a look at it!

    testproject.zip

  • Hello Tim, did you alread take a look at it? 

  • Hello Robin. I apologize but unfortunately I haven't been able to look at this yet. I should be able to in the next few days.
  • Hello Tim.

    No problem. I'm thankful for you taking the time at all.

  • Tim/Robin, Did you guys ever get to the bottom of this? It looks like I may be having a similar problem with peripheral+observer mode. I haven't even tried disabling the RF HALT but will try now. Thanks!
  • Hi Jonathan,

    I'm still having this problem and I'm still interested in a solution.

    But your case seems to be different, as for me it is working as long as RF HALT is enabled. (default is enabled I think)

    You could try to use the testproject.zip I attached to my post at November 24th.

    Maybe you could tell me if it works for you with line 367 of simpleBLEPeripheralObserver commented out or included or both. ;)

    So thanks for bringing this up again.

  • Hi Tim,

    do you have time sometime soon to take a look at it.

    I have it working with HCI_EXT_HALT_DURING_RF_ENABLE, but there are three reasons why I'm interested in disabling it.

    1. The device has to react to an external PWM signal with an interrupt with the least possible delay. To make this work, I currently have to give this interrupt a higher priority than the RF has. It is working this way, but I don't know how much this effects the bluetooth-handling.

    2. Our device needs to be scanning continuously, but there are also other things that need to be done regularly. So I need to set the scan window and interval as short as possible, to don't delay my eventhandling immensely. this leads to missing some of the advertises and of course still delays the execution of the eventhandler.

    3. I read in other threads that disabling the halt can help in case of connectivity problems. the corresponding smartphone-app sometimes has big problems connecting to the device.

    So the possibility to disable the halt would be a good opportunity to further optimize our device.

  • Any news about this topic ? I have the same issue here.

  • Yeah its a definite bug in the stack.  My use case was that I need to be connected and simultaneously scanning for advertisements.  When not connected I needed to be advertising and simultaneously scanning

    I can't remember the details now, but my comments in the code were this:

    // BUG WORKAROUND - If radio event is scheduled (i.e. advertising or connection interval)
    // then we can't start discovery b/c a bug will cause discovery to not be started for up
    // to the interval or will cause a reset of the micro.

    Basically if your discovery stops and then you schedule a discovery restart in a period less than your connection interval, then you could hang/reset.  Similarly if you're advertising and your discovery stops, then you must stop advertising before you can safely start discovery.    

    So I think their bug is likely if a stop discovery comes in and another one is added and scheduled to start, all in the time that another radio event is scheduled for an advertisement or connection event to occur.  I was able to pretty reliably determine this and fix it with a good level of confidence. 

    Here's a relevant portion of the handling after a discovery scan finishes:

    else if(gapMsgHdr->opcode == GAP_DEVICE_DISCOVERY_EVENT) {
          
    #ifdef DEBUG
          gapDevDiscEvent_t *ppp = (gapDevDiscEvent_t*)pMsg;
          last_scan_cnt = ppp->numDevs;
          if (last_scan_cnt > max_scan_cnt) {
            max_scan_cnt = last_scan_cnt;
          }
          total_restarts++;
    #endif
          
    #ifdef DEBUG_DISCOVERY
          P1DIR |= (1 << 4);
          P1_4 = 1;
    #endif
          discovering = FALSE;
               
          // BUG WORKAROUND - If radio event is scheduled (i.e. advertising or connection interval)
          // then we can't start discovery b/c a bug will cause discovery to not be started for up 
          // to the interval or will cause a reset of the micro.            
          uint8 state = GAPRole_GetState();
          osal_stop_timerEx(BLE_Bridge_TaskID, SBP_START_DISCOVERY);    // in case a delayed start was queued
          osal_clear_event(BLE_Bridge_TaskID, SBP_START_DISCOVERY);     // ... or it hit at same time as this event
          if (state == GAPROLE_CONNECTED || state == GAPROLE_CONNECTED_ADV) {
            uint16 interval;
            // When connected make sure that we wait at least twice the connect interval.  Imperical testing reveals
            // some kind of bug with the radio/LL scheduler when a stop and then start discovery happens in the time
            // frame that another radio event might have been scheduled.  Appears to be okay as long as the previously
            // scheduled radio event occurs, so wait the connect interval (2x safety added) before restarting
            GAPRole_GetParameter(GAPROLE_CONN_INTERVAL, &interval);
            if (interval < 13) {
              interval = 13;
            }
            interval = (interval * 2);
            osal_start_timerEx(BLE_Bridge_TaskID, SBP_START_DISCOVERY, interval);
          } else if (state == GAPROLE_ADVERTISING) {
            // Can't start scanning while advertising so stop and then that event will trigger discovery start followed by advertising start        
            GAPRole_SetAdvertising(FALSE);
          } else if (state == GAPROLE_WAITING || state == GAPROLE_WAITING_AFTER_TIMEOUT) {
            start_discovery_and_advertising();   // not advertising or connected so safe to start scanning now
          } else {
            // unexpected - not much to do except hope for best on reset which is 
            // sort of like re-enabling advertising and scanning
            HAL_ASSERT_FORCED();
            HAL_SYSTEM_RESET();
          }
        }
      }

    And here's this:

    /*********************************************************************
     * @fn      peripheralStateNotificationCB
     *
     * @brief   Notification from the profile of a state change.
     *
     * @param   newState - new state
     *
     * @return  none
     */
    static void peripheralStateNotificationCB( gaprole_States_t newState )
    {
      switch ( newState )
      {
        case GAPROLE_WAITING_AFTER_TIMEOUT:
        case GAPROLE_WAITING:
          {        
            // Probably just stopped advertising as part of the discovery restart process
            // So start discovery and then advertising back up
            start_discovery_and_advertising();
          }
          break;
        case GAPROLE_ADVERTISING:
          {
            HAL_ASSERT(discovering == TRUE);
          }
          break;
        case GAPROLE_CONNECTED_ADV:
        case GAPROLE_CONNECTED:
          {
             // Just in case we stopped discovering right before connecting... check for restart here
            start_discovery();
          }
          break;
          
        case GAPROLE_STARTED:
          { 
            start_discovery_and_advertising();
          }
          break;
    
        default:
          break;
    
      }
    }
    
    
    

    Some more:

    static __near_func void start_discovery(void)
    {
      bStatus_t retval;
      if (!discovering) {
        params.taskID = BLE_Bridge_TaskID;
        params.mode = DEVDISC_MODE_GENERAL;
        params.activeScan = SCAN_MODE;
        params.whiteList = FALSE;
      
        retval = GAP_DeviceDiscoveryRequest(&params);
        HAL_ASSERT(retval == SUCCESS);
        if (retval == SUCCESS) {
          discovering = TRUE;
    #ifdef DEBUG_DISCOVERY      
          P1_4 = 0;
    #endif
        }
      }
    }
    
    static __near_func void start_discovery_and_advertising(void)
    {
      start_discovery();  
      GAPRole_SetAdvertising(TRUE);
    }
    
    /*********************************************************************
     * @fn      BLE_Bridge_ProcessEvent
     *
     * @brief   SBLE_Bridge Application Task event processor.  This function
     *          is called to process all events for the task.  Events
     *          include timers, messages and any other user defined events.
     *
     * @param   task_id  - The OSAL assigned task ID.
     * @param   events - events to process.  This is a bit map and can
     *                   contain more than one event.
     *
     * @return  events not processed
     */
    __near_func uint16 BLE_Bridge_ProcessEvent( uint8 task_id, uint16 events )
    {
      if ( events & SYS_EVENT_MSG )
      {
        uint8 *pMsg;
    
        if ( (pMsg = osal_msg_receive( BLE_Bridge_TaskID )) != NULL )
        {
          BLE_Bridge_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );
    
          // Release the OSAL message
          VOID osal_msg_deallocate( pMsg );
        }
    
        // return unprocessed events
        return (uint16)(events ^ SYS_EVENT_MSG);
      }
    
      if ( events & SBP_START_DEVICE_EVT )
      {
        // Start the Device
        VOID GAPRole_StartDevice( &BLE_Bridge_PeripheralCBs );
    
        return (uint16)(events ^ SBP_START_DEVICE_EVT);
      }
      
      if (events & SBP_START_DISCOVERY) {
        // start discovery
        uint8 state = GAPRole_GetState();    
        if (state == GAPROLE_WAITING || state == GAPROLE_WAITING_AFTER_TIMEOUT || state == GAPROLE_STARTED) {      
          start_discovery_and_advertising();    // Advertising off so safe to start scanning and advertising now      
        } else if (state == GAPROLE_CONNECTED || state == GAPROLE_CONNECTED_ADV) {      
          // Only do discovery while connected - no advertising - this is safe b/c
          // we have allowed ample time since the last discovery end event to allow
          // and pending radio events while previously discovering to finish
          start_discovery();
        } else if (state == GAPROLE_ADVERTISING) {
          // if advertising and here DO NOT START or could cause hang - instead stop
          // advertising and we will restart discovery and advertising once stopped
          GAPRole_SetAdvertising(FALSE);      
        } else {      
          // unexpected - not much to do except hope for best on reset which is 
          // sort of like re-enabling advertising and scanning
          HAL_ASSERT_FORCED();
          HAL_SYSTEM_RESET();
        }
        
        return (uint16)(events ^ SBP_START_DISCOVERY);
      }
    
      // Discard unknown events
      return 0;
    }
    

    More...


    // What is the advertising interval when device is discoverable (units of 625us, 160=100ms)
    #define DEFAULT_ADVERTISING_INTERVAL 320

    #define SCAN_MODE FALSE

    // Whether to enable automatic parameter update request when a connection is formed
    #define DEFAULT_ENABLE_UPDATE_REQUEST TRUE

    // Limited discoverable mode advertises for 30.72s, and then stops
    // General discoverable mode advertises indefinitely
    #define DEFAULT_DISCOVERABLE_MODE GAP_ADTYPE_FLAGS_GENERAL

    // Minimum connection interval (units of 1.25ms, 80=100ms) if automatic parameter update request is enabled
    #define DEFAULT_DESIRED_MIN_CONN_INTERVAL 16

    // Maximum connection interval (units of 1.25ms, 800=1000ms) if automatic parameter update request is enabled
    #define DEFAULT_DESIRED_MAX_CONN_INTERVAL 20

    // Slave latency to use if automatic parameter update request is enabled
    #define DEFAULT_DESIRED_SLAVE_LATENCY 0

    And finally this... note that the comments here on times might be wrong as I changed them about 100 times...

    /*********************************************************************
     * PUBLIC FUNCTIONS
     */
    
    /*********************************************************************
     * @fn      BLE_Bridge_Init
     *
     * @brief   Initialization function for the BLE_Bridge App Task.
     *          This is called during initialization and should contain
     *          any application specific initialization (ie. hardware
     *          initialization/setup, table initialization, power up
     *          notificaiton ... ).
     *
     * @param   task_id - the ID assigned by OSAL.  This ID should be
     *                    used to send messages and set timers.
     *
     * @return  none
     */
    void BLE_Bridge_Init( uint8 task_id )
    {
      BLE_Bridge_TaskID = task_id;
    
      // Setup the GAP Peripheral Role Profile
      {
        uint8 initial_advertising_enable = FALSE;
    
        // By setting this to zero, the device will go into the waiting state after
        // being discoverable for 30.72 second, and will not being advertising again
        // until the enabler is set back to TRUE
        uint16 gapRole_AdvertOffTime = 0;
    
        uint8 enable_update_request = DEFAULT_ENABLE_UPDATE_REQUEST;
        uint16 desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
        uint16 desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;
        uint16 desired_slave_latency = DEFAULT_DESIRED_SLAVE_LATENCY;
        uint16 desired_conn_timeout = DEFAULT_DESIRED_CONN_TIMEOUT;
    
        // Set the GAP Role Parameters
        GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), (void *)&initial_advertising_enable );
        GAPRole_SetParameter( GAPROLE_ADVERT_OFF_TIME, sizeof( uint16 ), (void *)&gapRole_AdvertOffTime );
    
        GAPRole_SetParameter( GAPROLE_SCAN_RSP_DATA, sizeof ( scanRspData ), (void *)scanRspData );
        GAPRole_SetParameter( GAPROLE_ADVERT_DATA, sizeof( advertData ), (void *)advertData );
    
        GAPRole_SetParameter( GAPROLE_PARAM_UPDATE_ENABLE, sizeof( uint8 ), (void *)&enable_update_request );
        GAPRole_SetParameter( GAPROLE_MIN_CONN_INTERVAL, sizeof( uint16 ), (void *)&desired_min_interval );
        GAPRole_SetParameter( GAPROLE_MAX_CONN_INTERVAL, sizeof( uint16 ), (void *)&desired_max_interval );
        GAPRole_SetParameter( GAPROLE_SLAVE_LATENCY, sizeof( uint16 ), (void *)&desired_slave_latency );
        GAPRole_SetParameter( GAPROLE_TIMEOUT_MULTIPLIER, sizeof( uint16 ), (void *)&desired_conn_timeout );
      }
    
      // Set the GAP Characteristics
      GGS_SetParameter( GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, (void *)attDeviceName );
    
      // Set advertising interval
      {
        uint16 advInt = DEFAULT_ADVERTISING_INTERVAL;
    
        GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MIN, advInt );
        GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MAX, advInt );
        GAP_SetParamValue( TGAP_GEN_DISC_ADV_INT_MIN, advInt );
        GAP_SetParamValue( TGAP_GEN_DISC_ADV_INT_MAX, advInt );    
      }
      
      GAP_SetParamValue( TGAP_GEN_DISC_SCAN, 501 ); // Set scanning time to 4000ms - default is 10.24 seconds
      GAP_SetParamValue( TGAP_LIM_DISC_SCAN, 501 ); // Set scanning time to 4000ms - default is 10.24 seconds
        
      const uint16 scan_int = 15;
      GAP_SetParamValue( TGAP_CONN_SCAN_INT,      scan_int ); // Set scanning time to 4000ms - default is 480
      GAP_SetParamValue( TGAP_CONN_HIGH_SCAN_INT, scan_int ); // Set scanning time to 4000ms - default is 480
      GAP_SetParamValue( TGAP_GEN_DISC_SCAN_INT,  scan_int ); // Set scanning time to 4000ms - default is 480
      GAP_SetParamValue( TGAP_LIM_DISC_SCAN_INT,  scan_int ); // Set scanning time to 4000ms - default is 480
      GAP_SetParamValue( TGAP_CONN_EST_SCAN_INT,  scan_int ); // Set scanning time to 4000ms - default is 480
      
      const uint16 scan_wind = 15;
      GAP_SetParamValue( TGAP_CONN_SCAN_WIND,      scan_wind ); // Set scanning time to 4000ms - default is 240
      GAP_SetParamValue( TGAP_CONN_HIGH_SCAN_WIND, scan_wind ); // Set scanning time to 4000ms - default is 240
      GAP_SetParamValue( TGAP_GEN_DISC_SCAN_WIND,  scan_wind ); // Set scanning time to 4000ms - default is 240
      GAP_SetParamValue( TGAP_LIM_DISC_SCAN_WIND,  scan_wind ); // Set scanning time to 4000ms - default is 240
      GAP_SetParamValue( TGAP_CONN_EST_SCAN_WIND,  scan_wind ); // Set scanning time to 4000ms - default is 240
      
      GAP_SetParamValue( TGAP_FILTER_ADV_REPORTS, FALSE);
    
      // Initialize GATT attributes
      GGS_AddService( GATT_ALL_SERVICES );            // GAP
      GATTServApp_AddService( GATT_ALL_SERVICES );    // GATT attributes
      DevInfo_AddService();                           // Device Information Service
      SimpleProfile_AddService( GATT_ALL_SERVICES );  // Simple GATT Profile
      
      //disable halt during RF (needed for UART / SPI)
      HCI_EXT_OverlappedProcessingCmd(HCI_EXT_ENABLE_OVERLAPPED_PROCESSING);
      HCI_EXT_HaltDuringRfCmd(HCI_EXT_HALT_DURING_RF_DISABLE);
      HCI_EXT_ClkDivOnHaltCmd(HCI_EXT_DISABLE_CLK_DIVIDE_ON_HALT);
      HCI_EXT_SetTxPowerCmd(HCI_EXT_TX_POWER_4_DBM);
      HCI_EXT_SetRxGainCmd(HCI_EXT_RX_GAIN_HIGH);
          
    
      
      // Setup a delayed profile startup
      osal_set_event( BLE_Bridge_TaskID, SBP_START_DEVICE_EVT );	
    }
    

    Another tip is to add a "RESET" detection handling in the start of your code.  What I did was made a __no_init variable.  Then I do something like

    while (reset_detect_var == 0x12345678) {}

    reset_detect_var = 0x12345678;

    This will hang if the system ever resets, but on the first bootup it will not be 0x12345678 so will boot.  THis was critical to really get to the bottom of it.  Also toggling a pin on the scope for all events (in the code above) helped a lot too to understand what was going on.  Because just because it works, I noticed that if you have a long connection interval then your discovery could be delayed by as much as the connection interval (where no adverts are coming in even though you think discovery is enabled). I clearly could see this by pulsing a GPIO pin on every advert and another one right after the turn on event.

    Fun stuff.  I really wanted to bill my time to TI for all this grief.  

    Oh and I thought to myself through all this "well surely their demo of the combo role should work right?"  Well the reason it does is because they never automatically restart discovery.  So I suppose if they were to literally sit on the push button to immediately restart scanning every time then this might come into play.  

  • Thank you for your good effort Jonathan, I will try this at next opportunity.
    I'd still prefer to have TI fix this bug so no workaround is needed.
    Is somebody from TI still reading this?