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

SPI slave read without debugger

I am using nRF52840 DK as a SPI slave with an external MCU configured as SPI master. The SPI clock frequency is set to 1 MHz. I am using the following pins for SPI:

P0.29 - SPIS_CS_PIN P0.28 - SPIS_MISO_PIN P0.4 - SPIS_MOSI_PIN P0.3 - SPIS_SCK_PIN

Using the SPI slave driver in SDK 13.0.0 and S140 softdevice, I am seeing the SPI read is failing when the debugger is detached. SPI_XFER_DONE event does not happen. I am using a timer for SPI_XFER_DONE timeout purposes so that it does not get stuck waiting for SPI completion. The timeout is set to 100 millisecs (larger timeouts does not make any difference).

I start my SPI communication without debugger connection and I notice failures. Then in the middle of my test, I attach the debugger to running target and then communication is successful. Then as soon as I detach, the communication starts to fail again.

I am using IAR 8.11.1 as my IDE and segger J-Link as my debugger. The same behavior is seen if I use IAR I-Jet as my debugger also.

EDIT: 06/28/2017: If the low power mode was disabled (by commenting out sd_app_evt_wait() ), then this issue goes away. Setting constant latency mode to 1 did not make any difference. See attached code in the comments section.

  • Have you configured the chip in constant latency system on mode? See this answer for more details about wakeup times from sleep mode.

  • Yes. I configured it in the constant latency mode by using NRF_POWER->TASKS_CONSTLAT = 1 as the very first thing in the main. It did not make any difference.

    Also, I tried putting this just before entering the while(1) loop in the main. In this case, I did not even get a BLE connection. Not sure why this happens?

    Also, I commented out the sd_app_evt_wait inside the while(1) loop (i.e no low power mode). In this case SPI communication is normal and there are no issues when the debugger is not attached to the target.

    My main function is:

    int main(void)
    {
        uint32_t ticks;
    
        // 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;
    
        nrf_gpio_cfg_output(NRF_GPIO_PIN_MAP(0,8)); //nrf_gpio_cfg_output(NRF_GPIO_PIN_MAP(0,30));
    
    
        nrf_drv_spis_config_t spis_config = NRF_DRV_SPIS_DEFAULT_CONFIG;
    
        spis_config.csn_pin               = 6;//29;//APP_SPIS_CS_PIN;
        spis_config.miso_pin              = 22; //28;//APP_SPIS_MISO_PIN;
        spis_config.mosi_pin              = 20;//4;//APP_SPIS_MOSI_PIN;   15
        spis_config.sck_pin               = 24;//3;//APP_SPIS_SCK_PIN;
    
        APP_ERROR_CHECK(nrf_drv_spis_init(&spis, &spis_config, spis_event_handler));
    
        timer_init();
        ble_stack_init();
        gap_params_init();
        conn_params_init();
        gatt_init();
        advertising_data_set();
        server_init();
        client_init();
        gatt_mtu_set(m_test_params.att_mtu);
        data_len_ext_set(m_test_params.data_len_ext_enabled);
        conn_evt_len_ext_set(m_test_params.conn_evt_len_ext_enabled);
        preferred_phy_set(m_test_params.rxtx_phy);
        tx_power_set();
    
        advertising_start();
    
        for (;;)
        {
            wait_for_event();
    
            if(data_rdy)
            {
                // check if data is received over BLE
                data_rdy = false;
                //memset(m_rx_buf, 0, sizeof(m_rx_buf));
                spis_xfer_done = false;
                spis_xfer_timeout = false;
                ticks = nrf_drv_timer_ms_to_ticks(&TIMER_SPI, SPI_XFER_TIMEOUT_MS);   // 100 millisec timeout
                nrf_drv_timer_extended_compare(&TIMER_SPI, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
                nrf_drv_timer_enable(&TIMER_SPI);
    
                // respond to master with number of bytes received over BLE
                bytes_avail = m_tx_len;
                APP_ERROR_CHECK(nrf_drv_spis_buffers_set(&spis,
                                                         &bytes_avail,
                                                         1,
                                                         m_rx_buf,
                                                         1));
    
                //SET_TEST_POINT(31);
                while (!spis_xfer_done && !spis_xfer_timeout)
                {
                    wait_for_event(); //__WFE();
                }
    
                if(  spis_xfer_done )
                {
                    // respond to master with the actual bytes received over BLE (send TLV commmand)
                    spis_xfer_done = false;
                    spis_xfer_timeout = false;
                    ticks = nrf_drv_timer_ms_to_ticks(&TIMER_SPI, SPI_XFER_TIMEOUT_MS);   // 100 millisec timeout
                    nrf_drv_timer_extended_compare(&TIMER_SPI, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
                    nrf_drv_timer_enable(&TIMER_SPI);
    
                    APP_ERROR_CHECK(nrf_drv_spis_buffers_set(&spis,
                                                             m_tx_buf,
                                                             m_tx_len,
                                                             m_rx_buf,
                                                             m_tx_len));
    
                    //SET_TEST_POINT(31);
                    while (!spis_xfer_done && !spis_xfer_timeout)
                    {
                       wait_for_event(); // __WFE();
                    }
    
                    if( spis_xfer_done )
                    {
                        // read the response bytes from the master (receive TLV response)
                        spis_xfer_done = false;
                        spis_xfer_timeout = false;
                        ticks = nrf_drv_timer_ms_to_ticks(&TIMER_SPI, SPI_XFER_TIMEOUT_MS);   // 100 millisec timeout
                        nrf_drv_timer_extended_compare(&TIMER_SPI, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
                        nrf_drv_timer_enable(&TIMER_SPI);
                        APP_ERROR_CHECK(nrf_drv_spis_buffers_set(&spis,
                                                                 m_tx_buf,// dummy send
                                                                 m_tx_len,
                                                                 m_rx_buf,
                                                                 255));  // just read max possible
    
                        //SET_TEST_POINT(31);
                        while (!spis_xfer_done && !spis_xfer_timeout)
                        {
                            wait_for_event(); //__WFE();
                        }
                    }
                }
    
                nrf_gpio_pin_write(NRF_GPIO_PIN_MAP(0,8), 0); //nrf_gpio_pin_write(NRF_GPIO_PIN_MAP(0,30), 0); // clear the data ready line
    
                if(spis_xfer_done && (m_amts.notif_pkt_sz > 0) )
                {
                    // send if SPI transfer is success, do not send anything if timed out
                    nrf_ble_amts_notif_spam(m_rx_buf, &m_amts);
                    m_amts.notif_pkt_sz = 0;
                }
            }
        }
    }
    

    My wait_for_event function is:

        static void wait_for_event(void)
    
    {
        uint32_t  err_code;
    
        err_code = sd_app_evt_wait();
        APP_ERROR_CHECK(err_code);
    }
    
  • What device is the SPI master? Does it specify any required wakeup time for the slave device? Have you tried measuring the wakeup time of the nRF52840 device when configuring contant latency mode?

  • The SPI master is another MCU (MSP430FR5989). How do I measure the wakeup time for nRF52840?

  • You can configure the chip to wakeup from GPIO. Put the device in sleep mode (system on, constant latency), and use GPIOTE to set a GPIO on wakeup. Measure the time from wakeup GPIO is triggered until the other GPIO is set by GPIOTE.

Related