cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F3 SPI over DMA receive issues

G S
Associate II
Posted on January 08, 2018 at 22:05

Hello everyone,

I am using the STM32F303VC, trying to interface to a SPI EEPROM over DMA and having some issues on the reception of the data. The DMA is set up as full duplex, and I can see the transmissions and response fine when looking at the signals using a logic analyzer. The issue is in the DMA receive buffer. For some reason, the receive buffer is not showing the correct data. It appears that there's potentially some alignment issues, since I am seeing some of the correct values, just not so much in the right order. For example, I am

sending the following:

1.

MOSI: 0x06

MISO: 0xFF

2.

MOSI: 0x05 0xFF

MISO: 0xFF 0x02

3.

MOSI: 0x05 0xFF 0xFF 0xFF 0xFF

0xFF

0xFF

0xFF

MISO: 0xFF 0x02

0x02

0x02

0x02

0x02

0x02

0x02

0x02

And these are the contents of the receive buffer for each of the above cases

1. 0x00 (expected 0xFF)

2. 0x02 0x00 (expected 0xFF, 0x02)

3. 0x02 0xFF, 0x02, 0xFF, 0x02, 0x02, 0x02, 0x02 (expected 0xFF, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02)

Here's my initialization code, and function calls:

// Define for SPI DMA channels
#define EEPROM_SPI_DMA_RX_CHANNEL DMA1_Channel4 /* SPI2 RX DMA is handled by DMA 1 Channel 4 */
#define EEPROM_SPI_DMA_TX_CHANNEL DMA1_Channel5 /* SPI2 TX DMA is handled by DMA 1 Channel 5 */
void EEPROM_Initialize(void)
{
 GPIO_InitTypeDef GPIO_InitStructure;
 SPI_InitTypeDef SPI_InitStructure;
 DMA_InitTypeDef DMA_InitStructure;
 NVIC_InitTypeDef NVIC_InitStructure;
 // SPI CS, SCK, MISO and MOSI peripheral clock enable
 RCC_AHBPeriphClockCmd(EEPROM_SPI_CLK | EEPROM_SPI_CS_GPIO_CLK | EEPROM_SPI_SCK_GPIO_CLK |
 EEPROM_SPI_MISO_GPIO_CLK | EEPROM_SPI_MOSI_GPIO_CLK , ENABLE);
 // Enable DMA1 clock
 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
 
 // SPI Perihperal clock enable
 RCC_APB1PeriphClockCmd(EEPROM_SPI_CLK, ENABLE); 
 
 // Configure SPI pins: CS
 GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_CS_PIN;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
 GPIO_Init(EEPROM_SPI_CS_GPIO, &GPIO_InitStructure);
 
 GPIO_SetBits(EEPROM_SPI_CS_GPIO, EEPROM_SPI_CS_PIN); // Drive CS high
 
 // Configure SPI pins: SCK
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // Now that CS GPIO has been initialized to Mode: OUT, change GPIO Mode to AF
 GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_SCK_PIN;
 GPIO_Init(EEPROM_SPI_SCK_GPIO, &GPIO_InitStructure);
 
 // Configure SPI pins: MISO
 GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_MISO_PIN;
 GPIO_Init(EEPROM_SPI_MISO_GPIO, &GPIO_InitStructure);
 
 // Configure SPI pins: MOSI
 GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_MOSI_PIN;
 GPIO_Init(EEPROM_SPI_MOSI_GPIO, &GPIO_InitStructure);
 
 // Configure alternate function to SPI related GPIOs to act as SPI peripheral
 GPIO_PinAFConfig(EEPROM_SPI_SCK_GPIO, EEPROM_SPI_SCK_PIN_SOURCE, EEPROM_SPI_SCK_AF);
 GPIO_PinAFConfig(EEPROM_SPI_MISO_GPIO, EEPROM_SPI_MISO_PIN_SOURCE, EEPROM_SPI_MISO_AF);
 GPIO_PinAFConfig(EEPROM_SPI_MOSI_GPIO, EEPROM_SPI_MOSI_PIN_SOURCE, EEPROM_SPI_MOSI_AF);
 
 // Configure SPI
 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
 SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
 SPI_InitStructure.SPI_CRCPolynomial = 7;
 SPI_Init(EEPROM_SPI, &SPI_InitStructure);
 
 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // Enable DMA1 clock
 DMA_DeInit(EEPROM_SPI_DMA_TX_CHANNEL); // Reset DMA1 channe1 to default values;
 DMA_DeInit(EEPROM_SPI_DMA_RX_CHANNEL); // Reset DMA1 channe1 to default values;
 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // M2M Disabled- Peripheral mode (requires timer trigger)
 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // Normal mode
 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; // Medium priority
 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // Memory to Peripheral
 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 8-bit Register
 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Always write to same register
 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&EEPROM_SPI->DR; // Output data for SPI peripheral
 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 8-bit array
 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Increment through array
 DMA_InitStructure.DMA_MemoryBaseAddr = 0; // Initialize later
 DMA_InitStructure.DMA_BufferSize = 1; // Initialize later
 DMA_Init(EEPROM_SPI_DMA_TX_CHANNEL, &DMA_InitStructure); // Initialize TX DMA
 
 // Initialize RX DMA
 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // Peripheral to Memory
 
 DMA_Init(EEPROM_SPI_DMA_RX_CHANNEL, &DMA_InitStructure); // Initialize RX DMA
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?
void EEPROM_SPITransaction(uint8_t* commandData, uint8_t commandLength, const uint8_t* responseData, uint8_t responseLength)
{ 
 DMA_SetCurrDataCounter(EEPROM_SPI_DMA_TX_CHANNEL, commandLength);
 DMA_SetCurrDataCounter(EEPROM_SPI_DMA_RX_CHANNEL, responseLength);
 // Configure the peripheral base address
 EEPROM_SPI_DMA_TX_CHANNEL->CMAR = (uint32_t)commandData;
 EEPROM_SPI_DMA_RX_CHANNEL->CMAR = (uint32_t)responseData;
 /* The Data transfer is performed in the SPI using Direct Memory Access */
 /* Enable DMA SPI TX Stream */
 DMA_Cmd(EEPROM_SPI_DMA_TX_CHANNEL, ENABLE);
 /* Enable DMA SPI RX Stream */
 DMA_Cmd(EEPROM_SPI_DMA_RX_CHANNEL, ENABLE);
 // Assert the CS
 EEPROM_CS_Low();
 /* Enable SPI DMA TX Requsts */
 SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Tx, ENABLE);
 /* Enable SPI DMA RX Requsts */
 SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Rx, ENABLE);
 
 /* Enable the SPI peripheral */
 SPI_Cmd(EEPROM_SPI, ENABLE);
 /* Waiting the end of Data transfer */
 volatile unsigned int timeoutCounter = 0;
 while ((DMA_GetFlagStatus(DMA1_FLAG_TC5)==RESET) && (timeoutCounter < EEPROM_TIMEOUT))
 {
 timeoutCounter++;
 }
 timeoutCounter = 0;
 
 while ((DMA_GetFlagStatus(DMA1_FLAG_TC4)==RESET) && (timeoutCounter < EEPROM_TIMEOUT))
 {
 timeoutCounter++;
 }
 
 /* Clear DMA Flags */
 DMA_ClearFlag(DMA1_FLAG_GL4 | DMA1_FLAG_HT4 | DMA1_FLAG_TC4 | DMA1_FLAG_GL5 | DMA1_FLAG_HT5 | DMA1_FLAG_TC5);
 
 /* Disable DMA SPI TX Stream */
 DMA_Cmd(EEPROM_SPI_DMA_TX_CHANNEL,DISABLE);
 /* Disable DMA SPI RX Stream */
 DMA_Cmd(EEPROM_SPI_DMA_RX_CHANNEL,DISABLE); 
 
 /* Disable SPI DMA TX Requsts */
 SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Tx, DISABLE);
 /* Disable SPI DMA RX Requsts */
 SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Rx, DISABLE);
 /* Disable the SPI peripheral */
 SPI_Cmd(EEPROM_SPI, DISABLE);
 // Release the CS
 EEPROM_CS_High();
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

I'm driving my head against the wall on this one

:(

#spi #spi-dma #spi_rx #dma-spi-rx-receive #dma #stm32f3
3 REPLIES 3
Jan Waclawek
Senior II
Posted on January 10, 2018 at 07:13

Start a simple program with nothing else just the SPI.

Try to receive data without DMA, in a polled loop. Are they received correctly?

With DMA, try more varying data. Fill the buffer with a distinctively different pattern to see, where the received data start and whether the number of received data matches the number of transmitted data. Maybe you can disconnect the EEPROM and loop back MOSI to MISO.

JW

S.Ma
Principal
Posted on January 10, 2018 at 07:43

As SPI Master, I don't check for completion of the DMA TX, only on the completion of the DMA RX, then wait for SPI BUSY to be done and raise the NSS. For other first byte issue, it's probably configuring and sequencing. DMA RX settings should be initialized first, then DMA TX next (event that triggers the SPI clock generation)

G S
Associate II
Posted on January 18, 2018 at 14:46

Thanks for the replies!

It seems that the issue was resolved after adding the RX FIFO threshold to quarter full:

SPI_RxFIFOThresholdConfig(EEPROM_SPI,SPI_RxFIFOThreshold_QF);

Makes sense since I'm doing byte-wide transactions.