2024-05-30 08:39 AM
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:
SPI clock frequency: 700kHz
Here is an example of a successful transaction
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:
A similar situation can happen in the second part of a transaction. It looks like this:
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?
Solved! Go to Solution.
2024-05-31 06:41 AM
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!
2024-05-31 12:53 AM
Hello @Oleksandr
Could you please clarify what the signals Tx transaction and Rx transaction represent?
2024-05-31 02:07 AM
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);
}
2024-05-31 06:41 AM
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!
2024-05-31 06:49 AM
Hello @Oleksandr
Could you please share the errata and the fix you made please.
2024-05-31 07:35 AM
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:
For transmitting:
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.
2024-05-31 08:05 AM
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.
2024-07-05 06:37 AM
Hello @Oleksandr
The issue has been reported internally. Thank you for highlighting it to us.
Internal ticket number: 183071 (This is an internal tracking number and is not accessible or usable by customers).