cancel
Showing results for 
Search instead for 
Did you mean: 

SPI slave mode - data losing

Oleksandr
Associate III

Hello

I have two boards. Both of them are based on STM32 MCUs. One is a SPI master and the second is a SPI slave.

Settings for master (STM32H723):

    mHandle.Init.Mode                       = SPI_MODE_MASTER;
    mHandle.Init.Direction                  = SPI_DIRECTION_2LINES;
    mHandle.Init.DataSize                   = SPI_DATASIZE_8BIT;
    mHandle.Init.CLKPolarity                = mSclIdle == SclIdle::kLow ? SPI_POLARITY_LOW : SPI_POLARITY_HIGH;
    mHandle.Init.CLKPhase                   = mSclEdge == SclEdge::k1   ? SPI_PHASE_1EDGE  : SPI_PHASE_2EDGE;
    mHandle.Init.BaudRatePrescaler          = mPrescaler;
    mHandle.Init.FirstBit                   = SPI_FIRSTBIT_MSB;
    mHandle.Init.TIMode                     = SPI_TIMODE_DISABLE;
    mHandle.Init.CRCCalculation             = SPI_CRCCALCULATION_DISABLE;
    mHandle.Init.CRCPolynomial              = 0x00;
    mHandle.Init.NSS                        = SPI_NSS_SOFT;
    mHandle.Init.NSSPMode                   = SPI_NSS_PULSE_DISABLE;
    mHandle.Init.NSSPolarity                = SPI_NSS_POLARITY_LOW;
    mHandle.Init.FifoThreshold              = SPI_FIFO_THRESHOLD_01DATA;
    mHandle.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
    mHandle.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
    mHandle.Init.MasterSSIdleness           = SPI_MASTER_SS_IDLENESS_00CYCLE;
    mHandle.Init.MasterInterDataIdleness    = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
    mHandle.Init.MasterReceiverAutoSusp     = SPI_MASTER_RX_AUTOSUSP_DISABLE;
    mHandle.Init.MasterKeepIOState          = SPI_MASTER_KEEP_IO_STATE_ENABLE;
    mHandle.Init.IOSwap                     = SPI_IO_SWAP_DISABLE;

 

 Settings for slave(STM32L552):

    mHandle.Init.Mode           = SPI_MODE_SLAVE;
    mHandle.Init.Direction      = SPI_DIRECTION_2LINES;
    mHandle.Init.DataSize       = SPI_DATASIZE_8BIT;
    mHandle.Init.CLKPolarity    = SPI_POLARITY_LOW;
    mHandle.Init.CLKPhase       = SPI_PHASE_1EDGE;
    mHandle.Init.NSS            = SPI_NSS_SOFT;
    mHandle.Init.FirstBit       = SPI_FIRSTBIT_MSB;
    mHandle.Init.TIMode         = SPI_TIMODE_DISABLE;
    mHandle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    mHandle.Init.CRCPolynomial  = 7;
    mHandle.Init.CRCLength      = SPI_CRC_LENGTH_DATASIZE;
    mHandle.Init.NSSPMode       = SPI_NSS_PULSE_DISABLE;

So, settings for SPI: 

Oleksandr_0-1717081696365.png

SPI clock frequency: 700kHz

Here is an example of a successful transaction

Oleksandr_2-1717082783119.png

 

The CS signal drops to logic zero. Then the slave receives an instruction on what to do - read or write - and in the second part of the transaction performs this instruction. After the exchange of information, the CS signal returns to its original state.
The figure shows an example when a slave device receives a request to read 16 bytes of data from it.

 

Problem: sometimes the slave device loses data. It can process several hundred transactions correctly, but then a situation happens that it waits for 5 bytes as usually, these 5 bytes actually arrive because I see them with a logical analyzer, but the slave as if at the last byte, stops receiving and gets into an interrupt and stays there all the time until the next packet data (which occurs after some time) from the master.

It looks like this:

Oleksandr_3-1717083091971.png

 

A similar situation can happen in the second part of a transaction. It looks like this:

Oleksandr_5-1717083347815.png

 

I have tried to use pooling, interrupt, and DMA modes. The behavior is the same.

Why might there be similar behavior of the SPI bus in slave mode?

 

 

 

 

 

1 ACCEPTED SOLUTION

Accepted Solutions

Looks like I've fixed the problem.
For the first time in my life, I encountered such a situation that I needed to read the errata document. In all my practice, there have never been any problems with ST's MCU. So, according to this document, it is in SPI slave mode that the microcontroller has hardware issues that can be solved in software, but this is not done in the HAL library. I added the fix myself and the connection works stably.

 

Have a nice day, everyone!

View solution in original post

6 REPLIES 6
Saket_Om
ST Employee

Hello @Oleksandr 

 

Could you please clarify what the signals Tx transaction and Rx transaction represent?

If your question is answered, please close this topic by clicking "Accept as Solution".

Thanks
Omar

Of course.
I have two test points on my PCB. They are named accordingly:
TestPoint::Name::tpTxTransaction and TestPoint::Name::tpRxTransaction.
They're just GPO ports.

When I start the Read Transaction I set tpRxTransaction to 1 and when this transaction stops I set tpRxTransaction to 0. The same behavior for the tpTxTransaction test point and Transmit Transactions.

So, in the code it uses in this way (start):

/**
 * @brief SPI start transmiting
 *
 */
void Components::SpiSlave::z0()
{
    TestPoint::getInstance().set(TestPoint::Name::tpTxTransaction);

    mSpiS.transmit(mTxBuffer, mTransaction.len);
}

/**
 * @brief SPI start receiving
 *
 */
void Components::SpiSlave::z1()
{
    TestPoint::getInstance().set(TestPoint::Name::tpRxTransaction);

    mSpiS.receive(mRxBuffer, mTransaction.len);
}

 and the same for stop:

void Drivers::HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
    Drivers::SPIS::rxComplete(hspi);
    TestPoint::getInstance().rst(TestPoint::Name::tpRxTransaction);
}

void Drivers::HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
    Drivers::SPIS::txComplete(hspi);
    TestPoint::getInstance().rst(TestPoint::Name::tpTxTransaction);
}

 

Looks like I've fixed the problem.
For the first time in my life, I encountered such a situation that I needed to read the errata document. In all my practice, there have never been any problems with ST's MCU. So, according to this document, it is in SPI slave mode that the microcontroller has hardware issues that can be solved in software, but this is not done in the HAL library. I added the fix myself and the connection works stably.

 

Have a nice day, everyone!

Hello @Oleksandr 

 

Could you please share the errata and the fix you made please. 

If your question is answered, please close this topic by clicking "Accept as Solution".

Thanks
Omar

Link: STM32L552xx562xx errata 

Chapter: 2.16.2 BSY bit may stay high at the end of data transfer in slave mode

Page: 33

A fix for receiving:

Oleksandr_0-1717165590725.png

 

For transmitting:

Oleksandr_1-1717165768540.png

I don't like these changes. This is not a final decision. I will think about how to do it differently. The main thing is that it works as a proof-of-concept.

I think it will be better

/**
  * @brief  Handle the check of the RX transaction complete.
  * @PAram  hspi pointer to a SPI_HandleTypeDef structure that contains
  *               the configuration information for SPI module.
  * @PAram  Timeout Timeout duration
  * @PAram  Tickstart tick start value
  * @retval HAL status
  */
static HAL_StatusTypeDef SPI_EndRxTransaction(SPI_HandleTypeDef *hspi,  uint32_t Timeout, uint32_t Tickstart)
{
  if ((hspi->Init.Mode == SPI_MODE_MASTER) && ((hspi->Init.Direction == SPI_DIRECTION_1LINE)
                                               || (hspi->Init.Direction == SPI_DIRECTION_2LINES_RXONLY)))
  {
    /* Disable SPI peripheral */
    __HAL_SPI_DISABLE(hspi);
  }

  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //// BUG FIX
  //// BEGIN
  ////
  //// https://www.st.com/resource/en/errata_sheet/es0448-stm32l552xx562xx-device-errata-stmicroelectronics.pdf
  //// 2.16.2 BSY bit may stay high at the end of data transfer in slave mode
  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  // Check end-of-transaction flag
  uint32_t eotFlag = hspi->Init.Mode == SPI_MODE_MASTER ? SPI_FLAG_BSY : SPI_FLAG_RXNE;

  ///////////////////
  //// BUG FIX
  //// BEGIN
  ///////////////////

  if (SPI_WaitFlagStateUntilTimeout(hspi, eotFlag, RESET, Timeout, Tickstart) != HAL_OK)
  {
    SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
    return HAL_TIMEOUT;
  }

  if ((hspi->Init.Mode == SPI_MODE_MASTER) && ((hspi->Init.Direction == SPI_DIRECTION_1LINE)
                                               || (hspi->Init.Direction == SPI_DIRECTION_2LINES_RXONLY)))
  {
    /* Empty the FRLVL fifo */
    if (SPI_WaitFifoStateUntilTimeout(hspi, SPI_FLAG_FRLVL, SPI_FRLVL_EMPTY, Timeout, Tickstart) != HAL_OK)
    {
      SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
      return HAL_TIMEOUT;
    }
  }
  return HAL_OK;
}

/**
  * @brief  Handle the check of the RXTX or TX transaction complete.
  * @PAram  hspi SPI handle
  * @PAram  Timeout Timeout duration
  * @PAram  Tickstart tick start value
  * @retval HAL status
  */
static HAL_StatusTypeDef SPI_EndRxTxTransaction(SPI_HandleTypeDef *hspi, uint32_t Timeout, uint32_t Tickstart)
{
  /* Control if the TX fifo is empty */
  if (SPI_WaitFifoStateUntilTimeout(hspi, SPI_FLAG_FTLVL, SPI_FTLVL_EMPTY, Timeout, Tickstart) != HAL_OK)
  {
    SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
    return HAL_TIMEOUT;
  }

  Tickstart = HAL_GetTick();

  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //// BUG FIX
  //// BEGIN
  ////
  //// https://www.st.com/resource/en/errata_sheet/es0448-stm32l552xx562xx-device-errata-stmicroelectronics.pdf
  //// 2.16.2 BSY bit may stay high at the end of data transfer in slave mode
  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  if (hspi->Init.Mode == SPI_MODE_MASTER) {
	  /* Control the BSY flag */
	  if (SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_BSY, RESET, Timeout, Tickstart) != HAL_OK)
	  {
	    SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
	    return HAL_TIMEOUT;
	  }
  } else {
	  SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_BSY, RESET, SPI_SLAVE_DEFAULT_TIMEOUT, Tickstart);
  }

  ///////////////////
  //// BUG FIX
  //// END
  ///////////////////

  /* Control if the RX fifo is empty */
  if (SPI_WaitFifoStateUntilTimeout(hspi, SPI_FLAG_FRLVL, SPI_FRLVL_EMPTY, Timeout, Tickstart) != HAL_OK)
  {
    SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
    return HAL_TIMEOUT;
  }

  return HAL_OK;
}

For SPI_SLAVE_DEFAULT_TIMEOUT define added this preprocessor code:

#ifndef SPI_SLAVE_CUSTOM_DEFAULT_TIMEOUT
	#define SPI_SLAVE_DEFAULT_TIMEOUT 3U
#else
	#define SPI_SLAVE_DEFAULT_TIMEOUT SPI_SLAVE_CUSTOM_DEFAULT_TIMEOUT
#endif

Where SPI_SLAVE_CUSTOM_DEFAULT_TIMEOUT is a define that can be changed in the project properties.

So, as a result, I left the old code for the Master mode as is and added the new source code for the Slave mode.