AnsweredAssumed Answered

SPI RX buffer shifted

Question asked by salam.mark on Feb 23, 2018
Latest reply on Mar 9, 2018 by Ed Raab

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.

Outcomes