Skip to main content
Senior
October 4, 2019
Question

Receiving a burts of data from SPI using DMA

  • October 4, 2019
  • 11 replies
  • 1814 views

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.

This topic has been closed for replies.

11 replies

waclawek.jan
Super User
October 4, 2019

"Manual" (non-DMA) reception works?

JW

simo zzAuthor
Senior
October 4, 2019

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.

waclawek.jan
Super User
October 5, 2019

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

JW

simo zzAuthor
Senior
October 5, 2019

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
October 6, 2019

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

llo, World! Hello World! He

simo zzAuthor
Senior
October 6, 2019

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
October 6, 2019

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

simo zzAuthor
Senior
October 6, 2019

@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
October 6, 2019

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.

simo zzAuthor
Senior
October 6, 2019

Are you using DMA for it ?

So are you suggesting to use LL_SPI_NSS_HARD_INPUT and use an additional EXTI interrupt on it ? I will try it.

However I tried setting the CS low for whole 140 bytes and CS high and low for each byte and neither cases it worked fine. But I think the problems don't come from here since from the TRM manual it seems it can be done both ways.

s.

S.Ma
Principal
October 6, 2019

EXTI is independent of SPI and DMA, in the SPI, NSS as HW only put the SPI GPIO as tri-state when NSS is high to prevent disturbing the line if other slave is being addressed.

Yes, using DMA. Used HAL vs LL on STM32L4R9

1 Master and 12 slaves.

By the way, make sure the DMA RX is the one making events in slave mode, not TX.