cancel
Showing results for 
Search instead for 
Did you mean: 

[BUG_REPORT] Incorrect handling of SPI interrupts.

AGime.1
Associate II

Current implementation of the SPI transmit HAL function handles incorrectly interrupts. Actually the interrupt handler is implemented like this:

static void SPI_TxISR_8BIT(struct __SPI_HandleTypeDef *hspi)
{
  *(__IO uint8_t *)&hspi->Instance->DR = (*hspi->pTxBuffPtr);
  hspi->pTxBuffPtr++;
  hspi->TxXferCount--;
 
  if (hspi->TxXferCount == 0U)
  {
#if (USE_SPI_CRC != 0U)
    if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
    {
      /* Enable CRC Transmission */
      SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);
    }
#endif /* USE_SPI_CRC */
    SPI_CloseTx_ISR(hspi);
  }
}

At a first glance all seems correct, but this handling produces an error in a concrete scenario. Suppose you are acting as slave and a GPIO is used to signal the master when a one-byte message is ready to be transferred. The logical sequence to program this would be:

-Prepare a single byte interrupt-driven transfer.

-Signal the master that data is ready to be sent.

-Wait for transfer to complete.

As the HAL_SPI_Transmit_IT function triggers immediately the interrupt, the interrupt handler is executed immediately, the handler loads the byte in DR but because this is the last byte it calls to SPI_CloseTx_ISR which blocks the execution until the byte is transferred or a timeout happens. As the master has not been signaled about the prepared transfer this causes the timeout to expire and the data is never transferred.

Correcting this problem is very easy, and also the patch will avoid blocking during the duration of the last byte transfer of a multi-byte transfer

static void SPI_TxISR_8BIT(struct __SPI_HandleTypeDef *hspi)
{
  if (hspi->TxXferCount == 0U)
  {
#if (USE_SPI_CRC != 0U)
	if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
	{
	  /* Enable CRC Transmission */
	  SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);
	}
#endif /* USE_SPI_CRC */
	SPI_CloseTx_ISR(hspi);
	return;
  }
 
  *(__IO uint8_t *)&hspi->Instance->DR = (*hspi->pTxBuffPtr);
  hspi->pTxBuffPtr++;
  hspi->TxXferCount--;
}

Is just a matter of changing the order of execution, instead of testing if the last byte has been transmited, the test is placed before sending, with this the byte will be loaded into the fifo, the code will return the control to the main program, it will be able to signal the master and then once the byte has been really transferred the SPI_CloseTx_ISR function is executed.

This is also coded like this for the 16 bit handler so it should be also modified.

2 REPLIES 2
Amel NASRI
ST Employee

Hi @AGime.1​ ,

Thanks for reporting this case and providing the explanation as well as the patch.

I tracked this internally in order to be deeply reviewed by development team who should assess the required updates.

Internal ticket number: 132954 (This is an internal tracking number and is not accessible or usable by customers).

I noted that what you reported is applicable for STM32F4 and STM32G4.

Could you please precise the product you are using on your side?

-Amel

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

AGime.1
Associate II

Sure, I have found this problem using a STM32F401 and a STM32F411.

Cheers.