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
simo zz
Senior

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

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.

simo zz
Senior

Problem solved. It was due to the poor SPI cables quality and length in my prototype.

More precisely, the CS was randomly drop the logic level for a few ns.

I remounted a connector with a better cable and shorter length. Data is correctly received every time, without the need for EXTI interrupt.

Regards,

s.