cancel
Showing results for 
Search instead for 
Did you mean: 

Receiving a burts of data from SPI using DMA

simo zz
Senior

Hi,

I am using an STM32f407VGT MCU and I am trying to receive a burst of data of 112 bytes from SPI1 and storing it in an array buffer, initially set with all its elements to 0.

According to the RM0090 TRF this should be easily done with the DMA, but unfortunately I cannot see the data as correctly stored into the memory.

The code I am using to configure the DMA and the SPI is the following:

#include "stm32f4xx.h"
#include "stm32f4xx_ll_cortex.h"
#include "stm32f4xx_ll_bus.h"
#include "stm32f4xx_ll_dma.h"
#include "stm32f4xx_ll_rcc.h"
#include "stm32f4xx_ll_usart.h"
#include "stm32f4xx_ll_gpio.h"
#include "stm32f4xx_ll_system.h"
#include "stm32f4xx_ll_pwr.h"
#include "stm32f4xx_ll_spi.h"
#include "stm32f4xx_ll_tim.h"
#include "stm32f4xx_ll_utils.h"
 
 
#define SPI1_MOSI LL_GPIO_PIN_7
#define SPI1_MISO LL_GPIO_PIN_6
#define SPI1_SCLK LL_GPIO_PIN_5
#define SPI1_NSS LL_GPIO_PIN_4
 
uint8_t data_buffer[28][5] = {{0}};
 
void init_DMA(\
		DMA_TypeDef *DMAx, \
 
		uint32_t channel,
		uint32_t dma_stream,
		uint32_t src_address, \
 
		uint32_t dst_address, \
		uint32_t direction, \
		uint32_t mode, \
 
		uint32_t periph_inc_mode, \
		uint32_t mem_inc_mode, \
		uint32_t src_data_size, \
 
		uint32_t dst_data_size, \
		uint32_t nb_data, \
		uint32_t priority, \
 
		uint32_t fifo_mode, \
		uint32_t fifo_threshold, \
		uint32_t mem_burst, \
 
		uint32_t periph_burst)
{
	LL_DMA_InitTypeDef dma_init = {0};
 
	LL_DMA_StructInit(&dma_init);
	dma_init.Channel = channel;
	dma_init.PeriphOrM2MSrcAddress = src_address;
	dma_init.MemoryOrM2MDstAddress = dst_address;
	dma_init.Direction = direction;
	dma_init.Mode = mode;
	dma_init.PeriphOrM2MSrcIncMode = periph_inc_mode;
	dma_init.MemoryOrM2MDstIncMode = mem_inc_mode;
	dma_init.PeriphOrM2MSrcDataSize = src_data_size;
	dma_init.MemoryOrM2MDstDataSize = dst_data_size;
	dma_init.NbData = nb_data;
	dma_init.Priority = priority;
	dma_init.FIFOMode = fifo_mode;
	dma_init.FIFOThreshold = fifo_threshold;
	dma_init.MemBurst = mem_burst;
	dma_init.PeriphBurst = periph_burst;
 
	LL_DMA_EnableIT_TE(DMAx, dma_stream);
	LL_DMA_EnableIT_TC(DMAx, dma_stream);
	LL_DMA_Init(DMAx, dma_stream, &dma_init);
	LL_DMA_EnableStream(DMA2, dma_stream);
}
 
void DMA2_Stream0_IRQHandler(void)
{
	if(LL_DMA_IsActiveFlag_TC0(DMA2))
		LL_DMA_ClearFlag_TC0(DMA2);
}
 
void init_SPI(
		SPI_TypeDef *SPIx, \
		uint32_t transfer_dir, \
		uint32_t mode, \
		uint32_t data_width, \
		uint32_t clock_pol, \
		uint32_t clock_phase, \
		uint32_t nss, \
		uint32_t baudrate, \
		uint32_t bit_order, \
		uint32_t crc_calc, \
		uint32_t crc_poly)
{
	LL_SPI_InitTypeDef spi_init = {0};
 
	if(SPIx == SPI1)
	{
		init_GPIO(
				GPIOA, \
				SPI1_MOSI | SPI1_MISO | SPI1_SCLK | SPI1_NSS, \
				LL_GPIO_MODE_ALTERNATE, \
				LL_GPIO_SPEED_FREQ_VERY_HIGH, \
				LL_GPIO_OUTPUT_PUSHPULL, \
				LL_GPIO_PULL_UP, \
				LL_GPIO_AF_5);
	}
 
	LL_SPI_Disable(SPIx);
	LL_I2S_Disable(SPIx);
	spi_init.TransferDirection = transfer_dir;
	spi_init.Mode = mode;
	spi_init.DataWidth = data_width;
	spi_init.ClockPolarity = clock_pol;
	spi_init.ClockPhase = clock_phase;
	spi_init.NSS = nss;
	spi_init.BaudRate = baudrate;
	spi_init.BitOrder = bit_order;
	spi_init.CRCCalculation = crc_calc;
	spi_init.CRCPoly = crc_poly;
 
	if(SPIx == SPI1)
	{
        LL_SPI_EnableDMAReq_RX(SPI1);
	}
 
	LL_SPI_Init(SPIx, &spi_init);
	LL_SPI_Enable(SPIx);
}
 
int main(void)
{
	LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA | LL_AHB1_GRP1_PERIPH_GPIOB | LL_AHB1_GRP1_PERIPH_GPIOC | LL_AHB1_GRP1_PERIPH_GPIOD | LL_AHB1_GRP1_PERIPH_DMA1 | LL_AHB1_GRP1_PERIPH_DMA2);
	LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1 | LL_APB2_GRP1_PERIPH_SPI1);
 
	// DMA2 Channel 3 Stream 0 is attached to SPI1_RX
	init_DMA(DMA2, \
	        LL_DMA_CHANNEL_3, LL_DMA_STREAM_0, LL_SPI_DMA_GetRegAddr(SPI1), \
			(uint32_t) &data_buffer[0][0], LL_DMA_DIRECTION_PERIPH_TO_MEMORY, LL_DMA_MODE_CIRCULAR, \
			LL_DMA_PERIPH_NOINCREMENT, LL_DMA_MEMORY_NOINCREMENT, LL_DMA_PDATAALIGN_BYTE, \
			LL_DMA_MDATAALIGN_BYTE, 140, LL_DMA_PRIORITY_VERYHIGH, \
	        LL_DMA_FIFOMODE_ENABLE, LL_DMA_FIFOTHRESHOLD_1_4, LL_DMA_MBURST_SINGLE, \
	        LL_DMA_PBURST_SINGLE);
 
	init_SPI(SPI1, LL_SPI_SIMPLEX_RX, LL_SPI_MODE_SLAVE, LL_SPI_DATAWIDTH_8BIT, \
			LL_SPI_POLARITY_LOW, LL_SPI_PHASE_1EDGE, LL_SPI_NSS_HARD_INPUT, LL_SPI_BAUDRATEPRESCALER_DIV2, \
			LL_SPI_MSB_FIRST, LL_SPI_CRCCALCULATION_DISABLE, 10);
 
	NVIC_EnableIRQ(DMA2_Stream0_IRQn);
 
	while(1)
	{
        ;
	}
}

but every time I check the content of data_buffer it is always all to 0, like nothing has been stored into it.

So what am I doing wrong here ?

Any hint ?

Thanks in advance.

s.

12 REPLIES 12

"Manual" (non-DMA) reception works?

JW

simo zz
Senior

Communication without DMA works in the sense that data is received, but doesn't work as expected for what I have to do (continuous data burst of a certain amount of bytes to fill a memory buffer), in fact many times I cannot fill correctly the buffer due to CPU time I use to trace indexes and addresses of the memory buffer.

SPI + DMA should be perfect for my need.

Read out and check/post the SPI and relevant DMA registers (inlucing status registers).

JW

simo zz
Senior

Actually I can receive data using memory increment and circular mode this way:

init_DMA(DMA2,  LL_DMA_CHANNEL_3, LL_DMA_STREAM_0, LL_SPI_DMA_GetRegAddr(SPI1), (uint32_t) &data_buffer[0][0],  LL_DMA_DIRECTION_PERIPH_TO_MEMORY, LL_DMA_MODE_CIRCULAR, LL_DMA_PERIPH_NOINCREMENT, LL_DMA_MEMORY_INCREMENT, LL_DMA_PDATAALIGN_BYTE, LL_DMA_MDATAALIGN_BYTE, sizeof(data_buffer), LL_DMA_PRIORITY_VERYHIGH, LL_DMA_FIFOMODE_DISABLE, LL_DMA_FIFOTHRESHOLD_FULL, LL_DMA_MBURST_SINGLE, LL_DMA_PBURST_SINGLE);

But data is always shifted with random offset . I am not sure if this is a configuration issue...

I will check SPI and DMA status register as you propose.

s.

S.Ma
Principal

Do you use NSS edges to kick back the DMA to a start point?

llo, World! Hello World! He

simo zz
Senior

No. I assume that the LL_DMA_MODE_CIRCULAR should do the job fine...

If I start the DMA to source address &data_buffer[0][0] with circular mode, and data_buffer is 140 bytes long, why do I have to kick back the DMA to a start point ?

What kind of additional setup is required, if any ?

s.

S.Ma
Principal

Have you seen a slave SPI device work without NSS because it's not needed?

@S.Ma​ , the SPI1 slave device's NSS is configured as hard input pin. What is wrong with this ? I suppose nothing.

According to my code, shouldn't the DMA restart the source pointer after 140 bytes received ?

S.Ma
Principal

I'm now using STM32L4 (new IP version of SPI) and reset the SPI every NSS rise edge (EXTI interrupt).

12 MHz SPI, variable payload length running through a max 2 kbyte buffer array.

Works like a charm, even if the master or the slave don't have their power up synchronized, or if I'm putting a breakpoint during slave reception.