cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L151 SPI in interrupt mode unable to send from within interrupt

Alec Davis 2021
Associate III

STM32L151 running at 32MHZ

SPI prescaler 256 running at 125KBits/s

The interrrupt does fire, but the first call (from non interrupt context) to get the transfer started HAL_SPI_Transmit_IT hasn't yet finished.

Reason: In HAL_SPI_Transmit_IT __HAL_UNLOCK is called too late

The calling sequence is a follows:

1). main loop calls HAL_SPI_Transmit_IT( &hspi2, (uint8_t *)tx_buffer_ptr++, 1 )

2). Correct Data is sent (captured on logic analyser)

3). HAL_SPI_TxCpltCallback( ) is executed and calls HAL_SPI_Transmit_IT( &hspi2, (uint8_t *)tx_buffer_ptr++, 1 )

4). Data wasn't sent.

5). REASON: __HAL_LOCK at start of HAL_SPI_Transmit_IT fails, as initial call to HAL_SPI_Transmit_IT hadn't completed.

Taking the inspiration for the ADC HAL snippet below, and the comment regarding "potential interruption"

/* Process unlocked */
    /* Unlock before starting ADC conversions: in case of potential           */
    /* interruption, to let the process to ADC IRQ Handler.                   */
    __HAL_UNLOCK(hadc);

I changed the SPI to same idea.

function: HAL_SPI_Transmit_IT, bottom half
 
#if (USE_SPI_CRC != 0U)
  /* Reset CRC Calculation */
  if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
  {
    SPI_RESET_CRC(hspi);
  }
#endif /* USE_SPI_CRC */
 
   /* Process unlocked */
   /* Unlock before starting SPI transmissions: in case of potential           */
   /* interruption, to let the process to SPI IRQ Handler.                   */
  __HAL_UNLOCK(hspi);   //<<<<<<<<<< UNLOCK EARLY !!!!!
 
  /* Enable TXE and ERR interrupt */
  __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_TXE | SPI_IT_ERR));
 
  /* Check if the SPI is already enabled */
  if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
  {
    /* Enable SPI peripheral */
    __HAL_SPI_ENABLE(hspi);
  }
  return errorcode;   // <<<<<<< RETURN EARLY don't unlock again
 
 
error :
  __HAL_UNLOCK(hspi);
  return errorcode;
}

Now code works as expected.

Edit: All SPI functions would need fixing.

Also code reviewed STM32F4, STM32F1, STM32G0 (we use in another projects), they could potentially have the same problem, all of the ADC HAL code in these also call __HAL_UNLOCK early.

This SPI transfer stall could happen even on the faster processors if the first call to HAL_SPI_Transmit_IT is interrupted just before the __HAL_UNLOCK for a period longer than SPI transfer time, the SPI finishes the transfer and now the SPI interrupt fires while the SPI is still LOCKED.

Thanks

Alec Davis

3 REPLIES 3
Uwe Bonnes
Principal II

Probably transmit uses an interrupt with a lower priority that never gets call from inside an IRQ routine with higher priority. I thing transmit is blocking and using something blocking from inside an IRQ handler is no good idea. Perhaps set some signal in the receive handler and use the main program to wait for that signal to transmit.

Piranha
Chief II

A good example of how that HAL lock stupidity is not just broken, but also makes the driver less usable.

https://community.st.com/s/question/0D50X0000C5Tns8SQC/bug-stm32-hal-driver-lock-mechanism-is-not-interrupt-safe

Alec Davis 2021
Associate III

@MMAST.1​ 

You mentioned in this [BUG] STM32 HAL driver lock mechanism is not interrupt safe that fixes were coming.

Is there any movement on fixing the driver locks, or did the STM32L1 SPI HAL driver get missed?