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

best practices to nrf_drv_spi_transfer sequentially

Hello,

Currently, I am using the SDK15.2, BLE and two nRF52 DK boards, one using SPI master, and the other being the SPI Slave, between the SPIs and SPI master I added an extra line, allowing the slave to signal the master when it has data to send using a GPIO IRQ,

so basically when the Slave has data I set the GPIO line, and the Master enters quickly in the GPIO IRQ starting a nrf_drv_spi_transfer() non-blocking pulling the data from the Slave with no issues.

my issue:

1)when I am on the situation where I require to send  multiple transfers and as fast as possible I call multiple times nrf_drv_spi_transfer() from the master, but the slave is not able to receive all the transfers, it does not matter if I use a single byte, the slave is not able to process all them, if I dont use delays larger than 1ms between each nrf_drv_spi_transfer(), also if I leave uncommented the NRF_LOG_HEXDUMP_INFO all works fine ( it is in the code below and it adds some delays between each transfer too when calling NRF_LOG... functions, but I use it only for debug)

for example, multiples call to Send(buffer,buffer_size) on my master e.g

 Send(buffer1,buffer1_size);

 Send(buffer2,buffer2_size);

 Send(buffer3,buffer3_size);

where only I am able to receive the data from buffer1 and buffer3, if I have some delays or the NRF_LOG_HEXDUMP_INFO  printing, I receive the data from the 3 buffers, buffers1, buffers2, buffers3

I used an oscilloscope to check the MOSI line and it is writing the 3 buffers data, but for some reason, the slave is not receiving/processing all them, my code:

Master

void spi_event_handler(nrf_drv_spi_evt_t const * p_event, void * p_context)
	{ 
		transfer = false ; // the transfer is completed

		if (m_rx_buf[0] != 0)
		{				
			//NRF_LOG_INFO(" Receiving from Slave %d", m_rx_buf[1])
			//NRF_LOG_HEXDUMP_INFO(m_rx_buf, m_rx_buf[1]);
			message.data	= m_rx_buf;	
			message.lenght = m_rx_buf[1];	
			event(&message);		// where I process my message received from a SLAVE
		}
	}
	
	void spi_setup_init()
	{	
		nrf_drv_spi_config_t spi_config1 = NRF_DRV_SPI_DEFAULT_CONFIG;
		spi_config1.ss_pin		= SPI1_SS_PIN;
		spi_config1.miso_pin	= SPI1_MISO_PIN;
		spi_config1.mosi_pin	= SPI1_MOSI_PIN;
		spi_config1.sck_pin		= SPI1_SCK_PIN; 	
		spi_config1.frequency	= NRF_DRV_SPI_FREQ_8M;
		APP_ERROR_CHECK(nrf_drv_spi_init(&spi1, &spi_config1, spi_event_handler, NULL));		
	}
	
	void transmit(const uint8_t * data, uint16_t data_size)
	{
		while (transfer == true)
		{}	
		transfer = true;
		
		memset(m_rx_buf, 0, m_length);	

		//NRF_LOG_INFO(" Transmiting to Slave %d", data_size)
		//NRF_LOG_HEXDUMP_INFO(data, data_size);
		
		nrf_drv_spi_transfer(&spi1, data, m_length, m_rx_buf1, m_length);
		nrf_delay_us(1);	// I had to add this delay because I was getting corrupted the last bytes on the slave rx buffer
	}

Slave

void spis_event_handler(nrf_drv_spis_event_t event)
{
	if (event.evt_type == NRFX_SPIS_BUFFERS_SET_DONE)	
	{
		nrf_drv_gpiote_out_clear(Trigger_line);	// I use this to notify the master the slave has data ready to send
		NRF_LOG_INFO("NRFX_SPIS_BUFFERS_SET_DONE")
	}
	
	if (event.evt_type == NRF_DRV_SPIS_XFER_DONE)
	{
		APP_ERROR_CHECK(nrf_drv_spis_buffers_set(&spis, m_tx_buf, m_length, m_rx_buf, m_length));		
		
		//NRF_LOG_INFO(" Received from master %d", m_rx_buf[1])
		//NRF_LOG_HEXDUMP_INFO(m_rx_buf, m_rx_buf[1]);

		process_message();
		memset(m_rx_buf, 0, m_length);			
	}
}

void Initialize()
{ 
	nrf_drv_spis_config_t	spis_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;
	spis_config.mode         = NRF_SPIS_MODE_0;
	spis_config.bit_order    = NRF_SPIS_BIT_ORDER_MSB_FIRST;
	spis_config.csn_pullup   = NRFX_SPIS_DEFAULT_CSN_PULLUP;
	spis_config.miso_drive   = NRFX_SPIS_DEFAULT_MISO_DRIVE;
	spis_config.def          = NRFX_SPIS_DEFAULT_DEF;
	spis_config.orc          = NRFX_SPIS_DEFAULT_ORC;
	spis_config.irq_priority = NRFX_SPIS_DEFAULT_CONFIG_IRQ_PRIORITY;
	
	APP_ERROR_CHECK(nrf_drv_spis_init(&spis, &spis_config, spis_event_handler));
	
	memset(m_rx_buf, 0, m_length);
	APP_ERROR_CHECK(nrf_drv_spis_buffers_set(&spis, m_tx_buf, m_length, m_rx_buf, m_length));
	memset(m_tx_buf, 0, m_length);
	APP_ERROR_CHECK(nrf_drv_gpiote_init());
	
	nrf_drv_gpiote_out_config_t out_config;
	out_config.init_state	= NRF_GPIOTE_INITIAL_VALUE_LOW;
	out_config.task_pin		= false;
	APP_ERROR_CHECK(nrf_drv_gpiote_out_init(Trigger_line, &out_config));	
}

void Send(const uint8_t * data, uint16_t data_size)
{	

		memcpy(m_tx_buf, data, data_size);
		if (data[0] != requestcommand) //some messages are request from the master they does not enable the Trigger_line, all others does it
		{
			nrf_drv_gpiote_out_set(Trigger_line);
		}

		//NRF_LOG_INFO(" set buffer %d [%d]", data_size, x)
		//NRF_LOG_HEXDUMP_INFO(m_tx_buf, data_size);
}

2) which is the difference between SPI master and SPIM driver? does it depend on the nRF52 module in use(832 or 840)? are there some examples for SPIM Driver?

3) are there differences between SPI (clock rate, easy DMA) on nRF52832 and nRF52840?

Regards

Parents
  • 2) which is the difference between SPI master and SPIM driver?

    There are a couple of ways to use the SPI controller: using programmed I/O and using EasyDMA.

    Using the programmed I/O approach, you basically just write data a byte at a time into the SPI TX data register, and the SPI controller will wiggle the MOSI pin to match the bits. For completion, you can either test a status register in a loop or you can wait for a completion interrupt to trigger.

    With EasyDMA, you don't need to send each byte one at a time: you give the controller the address of the buffer you want to send and its length and then start a DMA transfer. The controller will clock all the bits out for the whole buffer. Again, you can test a status register for transfer completion or wait for a completion interrupt.

    Note that except for the SPIM4 controller in the nRF52840, you can only use EasyDMA to send 255 bytes at a time. With the SPIM4 controller on the nRF52840, I think this is increased to 65536 bytes. Also, SPIM4 supports clock speeds up to 32MHz. (The other controllers are limited to 8MHz.)

    I'm not sure of your context when you say "SPI master" but in a general sense, it implies the master side of the transfer (i.e. not the slave). SPIM implies that the controller is a master _and_ that is uses DMA instead of PIO for handling transfers.

    I think the older nRF52 design only supported the PIO mode, then they added EasyDMA mode for later chips. The same applies for the TWI (I2C) controller.

    3) are there differences between SPI (clock rate, easy DMA) on nRF52832 and nRF52840?

    The nRF52840 has one high speed SPIM controller (SPIM4) which supports clock speeds up to 32MHz. The nRF52832 is limited to 8MHz for all controllers. Also, the SPIM controllers on the nRF52832 can only transfer 255 bytes at a time via EasyDMA (larger buffers are sent using multiple transfers). The high speed SPIM controller on the nRF52840 supports up to 65535 byte transfers. (I'm not sure offhand if that also applies to the low speed controllers too.)

    -Bill

Reply
  • 2) which is the difference between SPI master and SPIM driver?

    There are a couple of ways to use the SPI controller: using programmed I/O and using EasyDMA.

    Using the programmed I/O approach, you basically just write data a byte at a time into the SPI TX data register, and the SPI controller will wiggle the MOSI pin to match the bits. For completion, you can either test a status register in a loop or you can wait for a completion interrupt to trigger.

    With EasyDMA, you don't need to send each byte one at a time: you give the controller the address of the buffer you want to send and its length and then start a DMA transfer. The controller will clock all the bits out for the whole buffer. Again, you can test a status register for transfer completion or wait for a completion interrupt.

    Note that except for the SPIM4 controller in the nRF52840, you can only use EasyDMA to send 255 bytes at a time. With the SPIM4 controller on the nRF52840, I think this is increased to 65536 bytes. Also, SPIM4 supports clock speeds up to 32MHz. (The other controllers are limited to 8MHz.)

    I'm not sure of your context when you say "SPI master" but in a general sense, it implies the master side of the transfer (i.e. not the slave). SPIM implies that the controller is a master _and_ that is uses DMA instead of PIO for handling transfers.

    I think the older nRF52 design only supported the PIO mode, then they added EasyDMA mode for later chips. The same applies for the TWI (I2C) controller.

    3) are there differences between SPI (clock rate, easy DMA) on nRF52832 and nRF52840?

    The nRF52840 has one high speed SPIM controller (SPIM4) which supports clock speeds up to 32MHz. The nRF52832 is limited to 8MHz for all controllers. Also, the SPIM controllers on the nRF52832 can only transfer 255 bytes at a time via EasyDMA (larger buffers are sent using multiple transfers). The high speed SPIM controller on the nRF52840 supports up to 65535 byte transfers. (I'm not sure offhand if that also applies to the low speed controllers too.)

    -Bill

Children
No Data
Related