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

nrf_drv_spi_transfer slow?

Hi,

When I call nrf_drv_spi_transfer to transfer 250 bytes in non-blocking mode, it takes 1.1ms. Is this normal? Any workaround? If it is normal then I have to split my state machine to more number of states so I can catch other coming interrupts.

Doesn't this nrf_drv_spi_transfer function write to a RAM buffer? If yes, I have a for loop before this function which transfers the exact same amount of bytes in 136us. Although, nrf_drv_spi_transfer is located in a function. Can the function overhead be this large?

Update: I am using nRF52832 with an external 20MHz crystal. And SDK version 11.

I use the following function to initialize SPI2

void application_spi2_init(void){
	nrf_drv_spi_config_t spi2_sd_config = NRF_DRV_SPI_DEFAULT_CONFIG(2);
	spi2_sd_config.ss_pin = 		SD_CS;
	spi2_sd_config.sck_pin = 		SD_SCLK;
	spi2_sd_config.mosi_pin = 		SD_MOSI;
	spi2_sd_config.miso_pin = 		SD_MISO;
	spi2_sd_config.frequency = 		NRF_DRV_SPI_FREQ_8M;													// TODO: this can be increased. The table on page 17 of the datasheet claims that the maximum speed is 20MHz.
	spi2_sd_config.mode = 			NRF_DRV_SPI_MODE_0;
	// NRF_SPI_MODE_0
	APP_ERROR_CHECK(nrf_drv_spi_init(&spi2_sd, &spi2_sd_config, application_spi2_sd_event_handler));
}

And in the handler:

void application_spi2_sd_event_handler(nrf_drv_spi_evt_t const * p_event){
// Triggers when the transfer is done
if(p_event->type == NRF_DRV_SPI_EVENT_DONE){
	sd_spi_state_machine = spi2_done;

	// Two state machines are handled here.......
}

}

In one of the states of one of the state machines I have the following code:

application_debug_set_pin(debug_pins_test2);
for(temp = 0; temp < 252; temp++){
    	commandDataBuffer[temp] = dataHolder->data[dataHolder->waitForSave][temp];
    }
err = application_spi_write_mutiple(commandDataBuffer, 252, sd_spi_nonblocking_mode);
application_debug_clear_pin(debug_pins_test2);

This debug_pins_test2 pin is the top signal in the photo at the end. And finally, the application_spi_write_mutiple function:

uint32_t application_spi_write_mutiple(uint8_t *data, uint8_t length, sd_spi_mode_type spi_mode){
uint32_t err;

application_debug_set_pin(debug_pins_test3);
sd_spi_state_machine = spi2_started;
err = nrf_drv_spi_transfer(&spi2_sd, data, length, NULL, 0); 	if(err != NRF_SUCCESS)		return err;
if(spi_mode == sd_spi_blocking_mode){
	while(sd_spi_state_machine != spi2_done);
}
application_debug_clear_pin(debug_pins_test3);
return err;

}

The debug_pins_test3 pin here is the second signal in the photo from oscilloscope. And here is the photo:

image description

And more update (08.May.2017):

I narrowed down my code to only a couple of lines. Here is my main

#include "nordic_common.h"
#include "nrf.h"
#include "softdevice_handler.h"
#include "app_util_platform.h"
#include "nrf_delay.h"
#include "app_error.h"
#include "nrf_gpio.h"
#include "nrf_drv_spi.h"
#include "application_spi2.h"

uint8_t 	commandDataBuffer[254];
int main(void){
	SystemInit();

    nrf_gpio_cfg_output(9);
    nrf_gpio_cfg_output(13);
	application_spi2_init();
while(1){
	    nrf_drv_spi_xfer_desc_t xfer_desc;
	    xfer_desc.p_tx_buffer = commandDataBuffer;
	    xfer_desc.p_rx_buffer = NULL;
	    xfer_desc.tx_length   = 252;
	    xfer_desc.rx_length   = 0;

		NRF_GPIO->OUTSET = (1 << 13);
	    nrf_drv_spi_xfer(&spi2_sd, &xfer_desc, 0);
		NRF_GPIO->OUTCLR = (1 << 13);
	    nrf_delay_ms(10);
	}
}

As can be seen I directly call nrf_drv_spi_xfer function and set/clear a pin right before and after this call. And I added a couple of lines to nrf_drv_spi.c to toggle another pin:

ret_code_t nrf_drv_spi_xfer(nrf_drv_spi_t     const * const p_instance,
                            nrf_drv_spi_xfer_desc_t const * p_xfer_desc,
                            uint32_t                        flags)
{

    NRF_GPIO->OUTSET = (1 << 9);
    spi_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];
    ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED);
    ASSERT(p_tx_buffer != NULL || tx_buffer_length == 0);
    ASSERT(p_rx_buffer != NULL || rx_buffer_length == 0);

    if (p_cb->transfer_in_progress)
    {
        NRF_GPIO->OUTCLR = (1 << 9);
        return NRF_ERROR_BUSY;
    }
    else
    {
        if (p_cb->handler && !(flags & (NRF_DRV_SPI_FLAG_REPEATED_XFER | NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER)))
        {
            p_cb->transfer_in_progress = true;
        }
    }

    p_cb->evt.data.done = *p_xfer_desc;
    p_cb->tx_done = false;
    p_cb->rx_done = false;

    if (p_cb->ss_pin != NRF_DRV_SPI_PIN_NOT_USED)
    {
        nrf_gpio_pin_clear(p_cb->ss_pin);
    }
    CODE_FOR_SPIM
    (
        return spim_xfer(p_instance->p_registers, p_cb,  p_xfer_desc, flags);

    )
    CODE_FOR_SPI
    (
        if (flags)
        {
            p_cb->transfer_in_progress = false;
            NRF_GPIO->OUTCLR = (1 << 9);
            return NRF_ERROR_NOT_SUPPORTED;
        }
        spi_xfer(p_instance->p_registers, p_cb, p_xfer_desc);
        NRF_GPIO->OUTCLR = (1 << 9);
        return NRF_SUCCESS;
    )
}

Well what do you expect? That the width of these two pulses are so close to each other, right?. This is what I get:

image description

How is it even possible, I have no clue. I tried many different things to find out what the problem is. The width of the longer pulse is almost equal to time needed for transferring all the bytes. I tested the whole thing with a NULL as event handler function in the SPI initializer. The width of the second pulse was then equal to the first one. As expected, since the SPI should perform in blocking mode. I am attaching the files that cleaned a bit to do the test. Let's see if you can recreate the problem. I would appreciate if you could share the hex file if you ran the code with no problem. application_spi2.h main.c Makefile nrf_drv_config.h nrf_drv_spi.c

Update: OK now I know that clearing the pin number 9 happens only after the SPI event handler is called. Although sending the data must take at least 1ms and returning from the function should be much faster.

I also analyzed the assembly code. Everything is as expected. Looked into the stack (which shouldn't overflow with this small program) and tracked the pc line by line. Nothing surprising, unfortunately.

Related