cancel
Showing results for 
Search instead for 
Did you mean: 

SPI RX buffer shifted

sirpak
Associate II
Posted on February 23, 2018 at 18:10

Dear All,

I use SPI as a master with 2 lines full duplex with DMA on STM32F303RE with 8-bit data size.

Frames are made of variable number of Bytes. Normally SPI clocks out 3 Bytes of command on MOSI line + 4 Bytes of reception which contains the slave answer.

My TX buffer is something like this:

   (CMD)(ADD_H)(ADD_L)(4 Bytes don't care)

My RX buffer will be:

   (3 Bytes don't care, normally 0xFFFFFF)(0x21)(0x43)(0x65)(0x87)

The output on the oscilloscope is perfect, but after a 30 seconds of millions of good communication I find the Rx buffer shifted:

(0x43)(0x65)(0x87)(0xFFFFFF)(0x21) or

(0x65)(0x87)(0xFFFFFF)(0x21)(0x43) or

(0x87)(0xFFFFFF)(0x21)(0x43)(0x65) or

The shifted conditions is kept for some second, then it change. If I let it go, it keep on shifting, till goes back in phase.

Here is the configuration of SPI:

SPI_I2S_DeInit(SPI2);

SPI2->CR1 = SPI_CPHA_1Edge | SPI_CPOL_Low | SPI_Mode_Master | SPI_BaudRatePrescaler_64 | SPI_FirstBit_MSB | SPI_NSS_Soft | SPI_Direction_2Lines_FullDuplex;

SPI2->CR2 = SPI_RxFIFOThreshold_QF | SPI_DataSize_8b | SPI_I2S_DMAReq_Tx | SPI_I2S_DMAReq_Rx;

Here the configuration of the two DMA channels (channel 4 RX and channel 5 TX):

// DMA channel Rx of SPI Configuration

DMA_DeInit(DMA1_Channel4);

DMA1_Channel4->CCR = DMA_DIR_PeripheralSRC | DMA_Mode_Normal | DMA_PeripheralInc_Disable | DMA_MemoryInc_Enable | DMA_PeripheralDataSize_Byte | DMA_MemoryDataSize_Byte | DMA_Priority_High | DMA_M2M_Disable;

DMA1_Channel4->CNDTR = 0;

DMA1_Channel4->CPAR = (uint32_t)&(SPI2->DR);

DMA1_Channel4->CMAR = (uint32_t)&RxBuffer[0];

// DMA channel Tx of SPI Configuration

DMA_DeInit(DMA1_Channel5);

DMA1_Channel5->CCR = DMA_DIR_PeripheralDST | DMA_Mode_Normal | DMA_PeripheralInc_Disable | DMA_MemoryInc_Enable | DMA_PeripheralDataSize_Byte | DMA_MemoryDataSize_Byte | DMA_Priority_Medium | DMA_M2M_Disable;

DMA1_Channel5->CNDTR = 0;

DMA1_Channel5->CPAR = (uint32_t)&(SPI2->DR);

DMA1_Channel5->CMAR = (uint32_t)&TxBuffer[0];

When I want to transmit and receive I created following function:

if((DMA1_Channel4->CNDTR != 0) || (DMA1_Channel5->CNDTR != 0))

   return 0;

if((SPI2->SR & (SPI_SR_FRLVL | SPI_SR_FTLVL | SPI_I2S_FLAG_RXNE | SPI_I2S_FLAG_BSY))

   return 0;

// Disable the SPI (trying to flush it)

SPI2->CR1 &= ~SPI_CR1_SPE;

// Disable the DMA channels to configure the CNDTR

DMA1_Channel5->CCR &= ~DMA_CCR_EN;

DMA1_Channel4->CCR &= ~DMA_CCR_EN;

// Enable SPI

SPI2->CR1 |= SPI_CR1_SPE;

// Configure odd transfers

SPI2->CR2 &= ~SPI_LastDMATransfer_TxOddRxOdd;

if((Tx_Size + Rx_Size) % 2)

   SPI2->CR2 |= SPI_LastDMATransfer_TxOddRxOdd;

// NSS signal to 0

ESC_CS_ON;

// Number of Bytes to transmit and receive

DMA1_Channel4->CNDTR = Tx_Size + Rx_Size;

DMA1_Channel5->CNDTR = Tx_Size + Rx_Size;

// Enable reception

DMA1_Channel4->CCR |= DMA_CCR_EN;

// Ebable transmission

DMA1_Channel5->CCR |= DMA_CCR_EN;

Another function checks the transfers are completed and take the data from the buffer

if((DMA1->ISR & (DMA1_FLAG_TC4 | DMA1_FLAG_TC5)) != (DMA1_FLAG_TC4 | DMA1_FLAG_TC5))

   return 0;

if((DMA1_Channel4->CNDTR != 0) || (DMA1_Channel5->CNDTR != 0))

   return 0;

if(SPI2->SR & (SPI_SR_FRLVL | SPI_SR_FTLVL | SPI_I2S_FLAG_RXNE | SPI_I2S_FLAG_BSY))

   return 0;

SPI2->CR1 &= ~SPI_CR1_SPE;

memcpy(value, (u8*)RxBuffer + Tx_Size, Rx_Size);

return 1;

My impression is that even though the SPI is disabled and enabled to try to flush it and even

though

 I check that the RXFIFO is empty, there is something that remains there and corrupt the future transfers.

#dma #spi #spi-flushing #spi-shift
11 REPLIES 11
HABOT
Associate III

SPI works with DMA without a single byte shifting. Just needed to create the whole project in an orderly fashion. Set the clock first, make all free pins as analog, then save. After configure peripherals, enable DMA on SPI, turn on ISR for DMA and off for SPI.

HABOT
Associate III

Hello all, good news! It seems the SPI byte shifting problem has been solved. The SPI DMA HAL in Slave Mode has been working for one day flawlessly. No byte shifting at all.

What did I do​?

I've created the whole project from the scratch by using CubeMX 5.1

After selecting the MCU from the list, ​CubeMX creates the project framework. Here are all the followed steps afterwards:

1) Go to project manager and define all the basic settings. Name for the project, application structure "Advanced", IDE, keep all default settings. Code Generator: copy only the necessary libraries, set all free pins as analog, keep all default settings.

2) Configure the clock and save the project.

3) Configure SPI (Master/Slave), activate DMA, turn on DMA interrupts, SPI interrupt off.

4) Save the project.

You​ need to move your custom files from the old project to the new one plus all the required configuration.

To​ make a test, the only method you need to call is:

HAL_SPI_TransmitReceive_DMA (in case of Full-Duplex)

This method makes all the necessary initialization inside.​

Guessing about why is it now working​, I believe that some resources need to be allocated first, maybe for the APB/AHB clocks, so the very first need to be the clock configuration.

After​ this I added the remaining peripherals I need for my project, and that is it.