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

SPIS not working with S140 softdevice on nRF52840

Hello,

we are developing a firmware for the nRF52840 which requires the use of Bluetooth and the SPIS (SPI Slave) functionality.

We are using S140 & SDK 16.

We encounter the following problem:

During init, we configure a 2 byte TX buffer for SPIS.

After init, the master initiates a 2 byte transaction.


With a logic analyzer we can observe that the nRF is correctly clocking out the 2 byte long buffer on MISO. 

But for every successive transaction, the nRF is clocking out the default byte (as defined in register DEF).

The SPIS event handler is an empty function, so it should not reconfigure anything.

We traced the problem to the function  nrf_pwr_mgmt_run(void)

void nrf_pwr_mgmt_run(void)
{
    PWR_MGMT_FPU_SLEEP_PREPARE();
    PWR_MGMT_SLEEP_LOCK_ACQUIRE();
    PWR_MGMT_CPU_USAGE_MONITOR_SECTION_ENTER();
    PWR_MGMT_DEBUG_PIN_SET();

 

    // Wait for an event.
#ifdef  SOFTDEVICE_PRESENT
    if (nrf_sdh_is_enabled())
    {
       ret_code_t ret_code = sd_app_evt_wait();
       ASSERT((ret_code == NRF_SUCCESS) || (ret_code == NRF_ERROR_SOFTDEVICE_NOT_ENABLED));
       UNUSED_VARIABLE(ret_code);
    }
    else
#endif // SOFTDEVICE_PRESENT
    {
        // Wait for an event.
        __WFE();
        // Clear the internal event register.
        __SEV();
        __WFE();
    }

 

    PWR_MGMT_DEBUG_PIN_CLEAR();
    PWR_MGMT_CPU_USAGE_MONITOR_SECTION_EXIT();
    PWR_MGMT_SLEEP_LOCK_RELEASE();
}

The SPIS works for successive transactions when we either

1. Don't call the nrf_power_mgmt_run(void) function
2. Comment out the lines '
PWR_MGMT_FPU_SLEEP_PREPARE();' and 'ret_code_t ret_code = sd_app_evt_wait();'

We therefore suspect that the call to sd_app_evt_wait() is the culprit.
Additionally, if we replace the call to nrf_power_mgmt_run(void) to a call to __WFE() the SPIS is also still working.

Is the SPIS not compatible with the BLE stack? Are we missing something else?
We would greatly appreciate any input on this problem.

Parents
  • Hi,

    The SPIS requires a bit more time to wake-up from low power System ON mode because it needs to wait for the HF clock to ramp up. This is why the SPIS example enables constant latency mode on startup:

        // 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;

    Is it possible to assert the CSN line a bit earlier in your case so the slave gets more time to wake-up? It will probably be the most optimal solution in terms of power consumption. The constant latency mode would increase the base current from a few uA to about 500 uA.

    Best regards,

    Vidar

  • Hello,

    We have the constant latency mode enabled.
    The chipselect is done by the hardware, but we alternatively tried setting the spi-clock to 1 kHz, that should give plenty of time.

    As you can see in the screenshot, the SPIS is still writing DEF (0xAA in our case).

    Edit: We added a delay ( > 2 us) between CS low and the first clock.

    In the following screenshot you can see the first transfer, which is working fine

    The next screenshot shows the second transfer, which is sending out DEF (0xAA)

      

    As far as our understanding goes, DEF is sent when the SPIS can't acquire the semaphore.

  • Hey,

    we also tried 100 uS and are getting the same result.

    I hope the following snippets are enough.

    static nrfx_spis_t const* spi_instance;
    
    static void*   spi_defaultContext;
    static uint8_t spi_defaultTxBuffer[2];
    static uint8_t spi_defaultRxBuffer[2];
    
    
    void spi_init( void )
    {
      nrfx_spis_config_t p_config   = NRFX_SPIS_DEFAULT_CONFIG;
      nrfx_spis_t        p_instance = NRFX_SPIS_INSTANCE( 0 );
      spi_instance                  = &p_instance;
      p_config.miso_pin             = BLE_MISO_PIN;
      p_config.mosi_pin             = BLE_MOSI_PIN;
      p_config.sck_pin              = BLE_SCLK_PIN;
      p_config.csn_pin              = BLE_CS_PIN;
      p_config.def                  = 0xAA;
      p_config.orc                  = 0xBB;
      p_config.mode                 = NRF_SPIS_MODE_0;
    
    
      nrfx_spis_init( spi_instance, &p_config, (nrfx_spis_event_handler_t)spi_evt_handler, spi_defaultContext );
    
      NRF_POWER->TASKS_CONSTLAT = 1;
    
      spi_defaultRxBuffer[0] = 0x00;
      spi_defaultRxBuffer[1] = 0x00;
    
      spi_defaultTxBuffer[0] = 0xDE;
      spi_defaultTxBuffer[1] = 0xAD;
    
      nrfx_spis_buffers_set( spi_instance, spi_defaultTxBuffer, 2, spi_defaultRxBuffer.controlSequence, 2 );
    }
    
    static void spi_evt_handler( nrfx_spis_evt_t const* p_event, void* p_contex )
    {
    // Empty
    }
    
    
    int main( void )
    {
      log_init( );
      timers_init( );
      power_management_init( );
      spi_init( );
      ble_stack_init_( );
      
      //...
    
      // Enter main loop.
      for( ;; )
      {
        if( NRF_LOG_PROCESS( ) == false )
        {
          nrf_pwr_mgmt_run( );
        }
      }
    }
    

  • Thanks. Looks like the TX buffer is set only once in the code you provided? It must be updated after every completed transaction by calling nrfx_spis_buffers_set().

  • We updated the handler as follows:

    static void spi_evt_handler( nrfx_spis_evt_t const* p_event, void* p_contex )
    {
      NRF_LOG_DEBUG( "Event: %d", p_event->evt_type);
      if( p_event->evt_type == NRFX_SPIS_BUFFERS_SET_DONE ) 
      {
        NRF_LOG_DEBUG( "Buffer set");
      }
      else if( ( p_event->evt_type == NRFX_SPIS_XFER_DONE ) )
      {
        NRF_LOG_DEBUG( "Transmission Done: %d, %d", p_event->rx_amount, p_event->tx_amount);
        APP_ERROR_CHECK( nrfx_spis_buffers_set( spi_instance, spi_defaultTxBuffer, 2, NULL, 0 ) );
      }
    }

    The result is the same.

    We also added some logging to the event handler.

    <debug> app: Event: 0
    <debug> app: Buffer set
    
    
    // snip
    
    <debug> app: Event: 1
    <debug> app: Transmission Done: 2, 2

    We only get one event of type NRFX_SPIS_BUFFERS_SET_DONE and one of type NRFX_SPIS_XFER_DONE. Nothing for the second transmission.

  • Found the problem:

    static nrfx_spis_t spi_instance = NRFX_SPIS_INSTANCE( 0 ); // <- declare here
    
    static void*   spi_defaultContext;
    static uint8_t spi_defaultTxBuffer[2];
    static uint8_t spi_defaultRxBuffer[2];
    
    void spi_init( void )
    {
      // snip
    
      nrfx_spis_buffers_set( spi_instance, spi_defaultTxBuffer, 2, spi_defaultRxBuffer.controlSequence, 2 );
    }

    We initialized p_instance in spi_init() and pointed the static pointer spi_instance to it.

    --> pointer to a stack location that gets overwritten.

    Thank you very much for the help!

Reply Children
No Data
Related