cancel
Showing results for 
Search instead for 
Did you mean: 

FIFO Fails to Clear on Abort - Slave SPI with DMA - STM32L451RE

AMacd.1
Senior

In a project using the STM32L451RET6, SPI3 is configured as follows
* Full Duplex Slave
* Hardware NSS Disable
* Frame Format Motoroloa
* Data Size 8
* CPOL High
* CPHA 2 Edge
* DMA:
  * SPI3_TX Ch2 Mem to Per, Priority Low
  * SPI3_RX Ch1 Per to Mem, Priority High

This works fine for a fixed packet length. Sometimes, however, the master may send a shorter packet (i.e. less than BUFFERLEN). In this case, it is necessary to end the slave SPI receive session and process the master's command. Consider the following code snippet:

#define NSS (HAL_GPIO_ReadPin( NSS_GPIO_Port, NSS_Pin ))
#define BUFFERSIZE 16

uint8_t SpiTxBuffer[BUFFERSIZE], SpiRxBuffer[BUFFERSIZE];

typedef enum
{
  Idle,
  Wait,
  Done,
  WaitForNSS,
  Error
} State_t;

State_t State = Idle;

int main(void)
{
  /**
    Usual MX boilerplate here.
    STM32L451REx
    SPI Slave on SPI3 with DMA, 8 bit data, soft NSS
  */
  while(1)
  {
    switch (State)
    {
      case Idle:
        if(!NSS)
        {
          State = Wait;
          if( HAL_SPI_TransmitReceive_DMA( &hspi3, SpiTxBuffer, SpiRxBuffer,
              BUFFERSIZE ) != HAL_OK )
          {
            Error_Handler();
          }
        }
        break;

      case Wait:
        if(NSS) /*Master ended SPI transfer early (shorter that BUFFERSIZE)*/
        {
          State = Done;
          if( HAL_SPI_Abort(&hspi3) != HAL_OK)
          {
            Error_Handler();
          }
        }
        break;

      case Done:
        State = WaitForNSS; /*In case BUFFERSIZE was received but NSS is slow*/
        break;

      case WaitForNSS:
        if(NSS) /*OK now we're sure we can begin to process command*/
        {
          DoSomethingWithTheData();
          State = Idle; /*Ready for next go around*/
        }
        break;

      default:
        Error_Handler();
        break;
    }
    DoSomethingElse();  /*Do some task while waiting for SPI to arrive*/
  }
}

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
  State = Done;
}

void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
  State = Error;
}

 

When the master sends the entire buffer all at once, no problems occur.

However, when the master raises NSS early (a shorter message), the return value of HAL_SPI_Abort() ends up being HAL_ERROR with the value HAL_SPI_ERROR_ABORT (0x40) appearing in hspi3.ErrorCode.

After some digging, I found that a breakpoint hits at line 4073 of stm32l4xx_hal_spi.c, in the function SPI_WaitFifoStateUntilTimeout(), called by SPI_EndRxTxTransaction at line 4134. This means that the receive fifo is not being emptied in 100ms!!! Strange since the master's clock is completely stopped at this point.

If I ignore the return value of HAL_SPI_Abort(), the subsequent HAL_SPI_TransmitReceive_DMA does not return HAL_OK. Plus, the data being shifted out is no longer correct.

I saw several references to adding the following lines after the abort completes (ignoring the return value)

__HAL_RCC_SPI3_FORCE_RESET();
__HAL_RCC_SPI3_RELEASE_RESET();

but this did not solve the problem.

I don't understand why the FIFO level bits are not going to zero. Shouldn't just 4 reads of the DR be enough to totally clear the FIFO?

 

 

0 REPLIES 0