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

BLE plus SPI Slave functionalities don't work at the same time?

Hi 

I am using an Analog Devices chip as a SPI master and BMD350 (based on nRF52832 chip) as a SPI slave. I want my phone app to send command to the BMD 350 (SPI Slave via Bluetooth) upon a request from the SPI Master and then the SPI Slave should respond with data/command it received from the phone via the MISO line while also receiving the data from the Master on the MOSI line and send it out to the phone app.

How I am trying to make this work: I have integrated the ble_app_uart example with the spis example from SDK 16.0.0 by creating a function called spi_init() and placing it in the main() of ble_app_uart and defining it similar to the spis example in the SDK. 

The issue I have is when I comment this part of the code, the BLE radio displays the device name for me to connect using my phone app but when I uncomment it, the BLE radio doesn't show the device name. It looks like only the SPI functionality or the Bluetooth functionality work at a time but not both together. I would appreciate if someone can help me understand how the ble and spi slave functionality can work together in the ble_app_uart example.

What is the function of this part of the code?

// while (1)
// {
// memset(m_rx_buf, 0, m_length);
// spis_xfer_done = false;
//
// APP_ERROR_CHECK(nrf_drv_spis_buffers_set(&spis, m_tx_buf, m_length, m_rx_buf, m_length));
//
// while (!spis_xfer_done)
// {
// __WFE();
// }
//
// NRF_LOG_FLUSH();
// NRF_LOG_INFO((char *)m_tx_buf);
//
// bsp_board_led_invert(BSP_BOARD_LED_0);
// }

Kind regards,
Tejas

  • static void spi_init(void)
    {
      //   Enable the constant latency sub power mode to minimize the time it takes
      //     for the SPIS peripheral to become active after the CSN line is asserted
      //     (when the CPU is in sleep mode).
      NRF_POWER->TASKS_CONSTLAT = 1;
      bsp_board_init(BSP_INIT_LEDS);
      
      APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
      NRF_LOG_DEFAULT_BACKENDS_INIT();
      
      printf("SPIS example");
      NRF_LOG_INFO("SPIS example");
      
      nrf_drv_spis_config_t spis_config = NRF_DRV_SPIS_DEFAULT_CONFIG;
      spis_config.csn_pin               = APP_SPIS_CS_PIN;    
      spis_config.miso_pin              = APP_SPIS_MISO_PIN;  
      spis_config.mosi_pin              = APP_SPIS_MOSI_PIN;  
      spis_config.sck_pin               = APP_SPIS_SCK_PIN;   
      
      APP_ERROR_CHECK(nrf_drv_spis_init(&spis, &spis_config, spis_event_handler));  
     // APP_ERROR_CHECK(nrf_drv_spis_buffers_set(&spis, m_tx_buf, m_length, m_rx_buf, m_length));
     /*add this */
    //  while (1)
    //    {
    //      memset(m_rx_buf, 0, m_length);
    //      spis_xfer_done = false;
    //      
    //      APP_ERROR_CHECK(nrf_drv_spis_buffers_set(&spis, m_tx_buf, m_length, m_rx_buf, m_length));
    //                               
    //      while (!spis_xfer_done)
    //      {
    //        __WFE();
    //      }
    //      
    //      NRF_LOG_FLUSH();
    //      NRF_LOG_INFO((char *)m_tx_buf);    
    //      
    //      bsp_board_led_invert(BSP_BOARD_LED_0);
    //    }
        /* add this */
    }

    This is the portion of code which I have added from the spis example provided by Nordic

    I have also edited the nus_data_handler as follows

    static void nus_data_handler(ble_nus_evt_t * p_evt)
    {
      
      if (p_evt->type == BLE_NUS_EVT_RX_DATA)
      {
        uint32_t err_code;
        
        NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on UART.");
        NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
     
     //   assert(strlen((char *)m_tx_buf) < UART_TX_BUF_SIZE);   /* TSP */
        
        for (uint32_t i = 0; i < p_evt->params.rx_data.length; i++)
        {
          do
          { 
            //strcat((char *)m_tx_buf, (char *)p_evt->params.rx_data.p_data[i]);    /* TSP */
            temp_ble_rx[i] = p_evt->params.rx_data.p_data[i];    /* TSP  create a temp buffer for received data from phone */
            
            err_code = app_uart_put(p_evt->params.rx_data.p_data[i]);
            
            if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY))
            {
              NRF_LOG_ERROR("Failed receiving NUS message. Error 0x%x. ", err_code);
              APP_ERROR_CHECK(err_code);
            }
          } while (err_code == NRF_ERROR_BUSY);
        }
        if (p_evt->params.rx_data.p_data[p_evt->params.rx_data.length - 1] == '\r')
        {
          while (app_uart_put('\n') == NRF_ERROR_BUSY);
        }
      }
      
      strcat((char *)m_tx_buf, (char *)temp_ble_rx);    /*transfering temp buffer contents into m_tx_buf */
      
      
    }

  • Hello,

    Do I understand you correctly that it is the spis_init function that contains the infinite while-loop?
    If so, I would recommend keeping the infinite loops to the main context, so as to be in keeping with the norms for C programming, and increase readability.
    Does it fail after you supply the buffers, or after you add the infinite while loop?
    Do you have access to a DK / external debugger, so that you may step through the code to see where it might get stuck?

    Where in your main function is this spis_init function called? I need to ask just to make sure, are you calling the spis_init before the ble_stack_init function?
    In that case, you will never return from the spis_init, and you will therefore never see the device advertise.

    Best regards,
    Karl

  • Yes, the spis_init() contains the infinite loop. 

    If I move the while loop into the main() where there is also a for(;;) { idle_state_handle(); }

    the compiler gives the error "Loop unreachable" because there are two infinite loops the while(1) and for(;;). The main() looks as shown below. spi_init() is called after uart_init()

    Could you clarify what you mean by "Does it fail after you supply the buffers, or after you add the infinite while loop?"

    I really appreciate your help. 

    int main(void)
    {
      bool erase_bonds;
      
      // Initialize.
      uart_init();
      spi_init();
      log_init();
      timers_init();
      buttons_leds_init(&erase_bonds);
      power_management_init();
      ble_stack_init();
      gap_params_init();
      gatt_init();
      services_init();
      advertising_init();
      conn_params_init();
      
      // Start execution.
      printf("\r\nUART started.\r\n");
      NRF_LOG_INFO("Debug logging for UART over RTT started.");
      advertising_start();
    
      // Enter main loop.
      for (;;)
      {
        idle_state_handle();  
      }

  • Hello,

    tp2k20 said:

    Yes, the spis_init() contains the infinite loop. 

    If I move the while loop into the main() where there is also a for(;;) { idle_state_handle(); }

    the compiler gives the error "Loop unreachable" because there are two infinite loops the while(1) and for(;;). The main() looks as shown below. spi_init() is called after uart_init()

    Thank you for clarifying.

    tp2k20 said:
    I really appreciate your help. 

    It is no problem at all, I am happy to help!

    tp2k20 said:
    Could you clarify what you mean by "Does it fail after you supply the buffers, or after you add the infinite while loop?"

    Never mind my question;
    The answer to your original ticket lies in the ordering of your main function - you are calling spi_init ( which contains an infinite loop ) as the second function of the main, way before you have started the SoftDevice and configured your BLE parameters. Because of this, whenever you have the infinite loop present in the spi_init function that function will never return, and the rest of the configuration is never done. Commenting out the loop in spi_init solves this issue - making BLE function again - because the rest of the main function then gets executed following spi_init returning.
    The compiler rightfully complains when it sees two infinite loops, because it inherently can not work.

    So, we thus need to take a look at how we can make this work using both SPIS and BLE at the same time. If you just move the contents of your SPIS_init while loop to the main while loop, this will not work either, because the SoftDevice will keep generating events, so we need to do something else.

    Then, what we instead may do in our main loop - since we know that this main loop will complete every so often because the SoftDevice will return the device from the idle_state_handler periodically ( and the SPIS module will generate events as well ) - then we may instead just create a check in the main loop, to see if anything has been received since the last time the main loop completed.
    Then, if a transfer has been received, that data will need to be handled before it goes back to the idle_state_handler.
    How does this sound?

    Side note; when you are using the legacy spi driver for spis, you are in fact using the newer nrfx_spis driver behind the scenes, since the legacy driver in the SDK v16 is replaced by forwarding macros to the nrfx_spis driver. Therefore, you may as well change all your naming to match the nrfx_spis driver directly, to skip the inclusion of the legacy driver entirely.
    This will not matter in any way for the current implementation, but it will save you future work if there is ever a new SDK release that does not contain the legacy driver.
    The great news here is that the nrfx_spis is implemented using easyDMA - which makes this whole issue a lot easier. easyDMA lets the SPIS peripheral continue receiving data even though the SoftDevice has taken control over the CPU.

    For future reference, I would generally and strongly advise against adding an infinite loop as part of a function - this is what the main loop is for.

    Best regards,
    Karl 

Related