Time gap between each spi_transceive

Hi,

I am working on optimize the SPI timing for an NFC reader IC which connected to nRF52840, to minimize the read/write time as much as possible.

And I notice that the time between each read/write reg takes about 40us when spi-max-frequency = <8000000>.

Then I wrote a code snippet to call spi_transceive directly:

uint8_t start_reg = 0x74;
uint8_t reg_value = 0;
const struct spi_buf tx_buf = {
	.buf = &start_reg,
	.len = 1
};
const struct spi_buf_set tx = {
	.buffers = &tx_buf,
	.count = 1
};

/* Read register value. */
const struct spi_buf rx_bufs[] = {
	{.buf = NULL, .len = 1},
	{.buf = &reg_value, .len = 1}
};
const struct spi_buf_set rx = {
	.buffers = rx_bufs,
	.count = ARRAY_SIZE(rx_bufs)
};

int test_reg_read(void)
{
	int err = spi_transceive(spi_dev, &spi_cfg, &tx, &rx);
	if (err) {
		LOG_ERR("SPI reg read failed, err: %d.", err);
		return err;
	}
	return 0;
}

// In a seperate thread
for (size_t i = 0; i < 200; i++) {
	test_reg_read();
}

With the code, the SPI waveform like this:

28us between each read/write.

Is it possible to reduce the time between each spi read/write?

I am using ncs2.5.2

Regards,

Anthony

Parents
  • Hello Anthony,

    Is there a particular reason why you are reading one byte over and over?

    Please note that the 8MHz refers to the clock speed, so doing multiple transactions, you will not be able to transfer data with that speed.

    If you want to read at 8MHz, you would need to send a read command with a longer buffer. Then this entire buffer should be transferred at 8MHz, but you will see that there is a command being sent first, to prepare the sensor, followed by a small delay (like the one you see at roughly 10µs), and then it will be followed by the actual data at 8MHz rate.

    When you do multiple reads of 1 byte, like you are doing there, there are lots of things going on between each read, like setting up and preparing the SPI peripheral for the next transaction, and this will take some time.

    Is there some particular thing that you are trying to acheive? Let me know, and perhaps we can think of something that can work.

    Best regards,

    Edvin

Reply
  • Hello Anthony,

    Is there a particular reason why you are reading one byte over and over?

    Please note that the 8MHz refers to the clock speed, so doing multiple transactions, you will not be able to transfer data with that speed.

    If you want to read at 8MHz, you would need to send a read command with a longer buffer. Then this entire buffer should be transferred at 8MHz, but you will see that there is a command being sent first, to prepare the sensor, followed by a small delay (like the one you see at roughly 10µs), and then it will be followed by the actual data at 8MHz rate.

    When you do multiple reads of 1 byte, like you are doing there, there are lots of things going on between each read, like setting up and preparing the SPI peripheral for the next transaction, and this will take some time.

    Is there some particular thing that you are trying to acheive? Let me know, and perhaps we can think of something that can work.

    Best regards,

    Edvin

Children
  • Hi Edvin,

    Thanks for your reply.

    I did not make it clear:

    I wrote my own NFC IC spi driver take nrf/lib/st25r3911b/st25r3911b_spi.c as an example.

    When transfer data in one transaction, the speed is normal:

    As same as the 3911b driver, the NFC IC I use need read/write several 1-byte registers between each data transaction like st25r3916b_reg_write() did.

    And these read/write 1-byte register commands takes more time than the actual data transaction as the cost between transactions you mentioned.

    I understand that zephyr spi driver need to handle config/buffers/SS pin and move data around before and after, is it normal for around 30us between each transactions?

    Maybe I need to give up zephyr spi driver and call legacy nrf api directly to minimize the overall transaction time?

    Let me know if you have any suggestions.

    Regards,

    Anthony Yuan

  • Hello Anthony,

    Ok, it makes a bit more sense with this context.

    You could try to use the nrfx_spim driver. I have not done the measurements myself, but if it is not any faster, at least it opens up for using some PPI trickery to speed things up. You can fill up some buffers, and then start the next transaction based on the event that the previous transaction is complete. 

    If you want to give it a go, you can set up the SPI like this. Remember to remove/disable all "normal" zephyr SPI things, and to disable it in an overlay file, so that the nrfx driver can access the SPI instance that you are using.

    #define SPIM_INST 1
    
    uint8_t spi_tx_buffer[1] = { 0x00 };
    uint8_t spi_rx_buffer[1];
    
    
    void gpio_init()	// set up control for the CS pin.
    {
    	int ret;
    	const struct device *port0;
    	port0 = DEVICE_DT_GET(DT_NODELABEL(gpio0));
    
    	if(port0 == NULL){
    		LOG_ERR("port0 not found\n");
    	}
    
    	ret = gpio_pin_configure(port0, SPI_CS_PIN, GPIO_OUTPUT);
    	if (ret < 0) {
    		LOG_ERR("gpio_pin_configure returned %x", ret);
    	}
    
    	gpio_pin_set(&port0, SPI_CS_PIN, 1);
    }
    
    void my_spi_init()
    {
    	nrfx_err_t ret;
    
    	IRQ_CONNECT(DT_IRQN(DT_NODELABEL(spi1)),
    				DT_IRQ(DT_NODELABEL(spi1), priority),
    				nrfx_isr, nrfx_spim_1_irq_handler, 0);	
    	
    	nrfx_spim_t spim_inst = NRFX_SPIM_INSTANCE(SPIM_INST);
    
    	nrfx_spim_config_t spim_config = NRFX_SPIM_DEFAULT_CONFIG(
    		SCK_PIN,
    		MOSI_PIN,
    		MISO_PIN,
    		NRF_SPIM_PIN_NOT_CONNECTED);	// Control CS pin manually
    
    	spim_config.frequency = 8000000;
    	
    	ret = nrfx_spim_init(&spim_inst, &spim_config, NULL, p_context); // You can set an spim handler if you want the spi to be non-blocking. I suspect that would be slower. 
    
    	if (ret != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_spim_init returned %08x", ret);
    	}
    	
    }
    
    void my_spi_transfer
    {
    	nrfx_err_t ret;
    
    	gpio_pin_set(&port0, SPI_CS_PIN, 0);
    	nrfx_spim_xfer_desc_t spim_xfer_desc = NRFX_SPIM_XFER_TRX(spi_tx_buffer, sizeof(spi_tx_buffer),	spi_rx_buffer, sizeof(spi_tx_buffer));
    	
    	ret = nrfx_spim_xfer(&spim_inst, &spim_xfer_desc, 0);
    	if (ret != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_sipm_xfer returned %08x", ret);
    	}
    }
    
    

    Best regards,

    Edvin

Related