2025-09-12 9:42 AM
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?