Skip to main content
Senior II
December 15, 2021
Solved

Best way to use DMA without knowing size of SPI transfer ?

Hi,

device : STM32G473VBT6

What is the best way to process SPI data when we don't know how many bytes will come from the SPI master ?I use the SPI in full duplex slave.

The problem with DMA is that the DMA buffer is circular, where my processing functions are not. I don't have the time to copy bytes to another buffer, and code to access the circular buffer slows down significantly when processing 320 bytes .

i need to access the buffer as fast as i can.

I have used an easy solution ,see under "// reset spi to flush FIFO" , this reset the SPI every interrupt and set the DMA buffer back to element 0 of spi_buf_in.

The disadvantage is that this reset also takes 40µs at 170 Mhz.

This works mostly perfectly, but sometimes after 1000's SPI transfers my spi_buf_in data is corrupt (compared to the data i see trough the logic analyzer)

My guess is that this corruption comes because of the unconventional reset of the SPI.

How do i read incoming SPI data most elegantly at the highest speed ?

// SPI enable line will have positive flank once the data is transferred from PI to STM, this will be used as interrupt to
 
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
	uint16_t SPISize;
	uint32_t head;
 
	if(GPIO_Pin == GPIO_PIN_12){ // spi enable line interrupt at positive edge
 
		head = __HAL_DMA_GET_COUNTER(phMainSPI->hdmarx); // get number of bytes received by the SPI rx DMA
		SPISize = (uint16_t) (SPI_SIZE - head);
 
 // processing spi_buf_in data ........
 
		// reset spi to flush FIFO
	 HAL_SPI_DeInit(phMainSPI);
	 __HAL_RCC_SPI2_FORCE_RESET();
	 __HAL_RCC_SPI2_RELEASE_RESET();
	 HAL_SPI_Init(phMainSPI);
		HAL_SPI_TransmitReceive_DMA(phMainSPI,spi_buf_out,spi_buf_in,320);
	}
}

This topic has been closed for replies.
sde c.1Best answer
Senior II
January 19, 2022

this seems to do the trick, takes only 11µs and reset DMA pointer to the beginning of the buffer

#define RESETSPI {\
	HAL_SPI_Abort(&hspi2);\
	__HAL_RCC_SPI2_FORCE_RESET();\
	__HAL_RCC_SPI2_RELEASE_RESET();\
}

9 replies

waclawek.jan
Super User
December 15, 2021

You don't need to reinitialize SPI, just stop/start the Rx DMA, as outlined in RM. Basically it's: clear DMA_CCRx.EN, wait until it clears, reset status flags if needed, set DMA_CCRx.EN.

I don't know how to do that in Cube, I don't Cube.

JW

sde c.1Author
Senior II
December 22, 2021

i tried but that doesn't work, i use the HAL library and cube, i think i must initialise much more in that case

TDK
December 15, 2021

> How do i read incoming SPI data most elegantly at the highest speed ?

Set it up as a circular DMA transfer so you never need to deinitialize the peripheral. Poll for the number of characters available in the ring buffer by reading the NDTR register and keeping track of the head/tail pointers.

"If you feel a post has answered your question, please click ""Accept as Solution""."
waclawek.jan
Super User
December 15, 2021

If I understood correctly, problem is in processing the received data, in situ for speed. Many algorithms become a nightmare should they account for wraparound - think FFT.

JW

waclawek.jan
Super User
December 22, 2021

With circular DMA you don't need to stop/reinitialize, simply remember where in buffer was the end of the previous chunk of data and after receiving next chunk of data, start processing there.

If you don't want to use circular DMA but you want stop/start, and using Cube/HAL does not work or takes too long, well then don't use Cube/HAL.

JW

TDK
December 22, 2021

> I just need an easy and fast way (faster then 40µs) to initialise the DMA to write at the beginning of the buffer each SPI CS interrupt . Any idea how to do that?

Use register level access. Stop the peripheral, reset the DMA for the new transfer, and start it again. HAL_SPI_DeInit does many more things than just stop the peripheral, such as deinitializing pins.

There is probably something that can be done with HAL_SPI_DMAStop as well, but I don't know the details or if it will execute in <40us.

As JW says, if HAL is too slow, don't use it.

I personally think the circular buffer is a better solution and that memory management is straightforward to check when the buffer wraps around and when it doesn't. You will occasionally lose time copying data, but you will save time by not re-initializing SPI/DMA. Copying 320 bytes is fast.

"If you feel a post has answered your question, please click ""Accept as Solution""."
sde c.1AuthorBest answer
Senior II
January 19, 2022

this seems to do the trick, takes only 11µs and reset DMA pointer to the beginning of the buffer

#define RESETSPI {\
	HAL_SPI_Abort(&hspi2);\
	__HAL_RCC_SPI2_FORCE_RESET();\
	__HAL_RCC_SPI2_RELEASE_RESET();\
}