2021-05-25 07:05 PM
MCU is STM32F427. I'm using SPI3 to read from a transmit only slave. My configuration looks like this:
/* SPI3 parameter configuration*/
hspi3.Instance = SPI3;
hspi3.Init.Mode = SPI_MODE_MASTER;
hspi3.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
hspi3.Init.DataSize = SPI_DATASIZE_16BIT;
hspi3.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi3.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi3.Init.NSS = SPI_NSS_SOFT;
hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi3.Init.TIMode = SPI_TIMODE_DISABLE;
hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi3.Init.CRCPolynomial = 10;
When it comes time to read the code does the following:
Stepping through the code in (2), I see the correct data received in two 8 bit reads. However, when the HAL code gets to the end of HAL_SPI_Receive, it does the following.
/* Check the end of the transaction */
if (SPI_EndRxTransaction(hspi, Timeout, tickstart) != HAL_OK)
{
hspi->ErrorCode = HAL_SPI_ERROR_FLAG;
}
This sets the error code every time and the HAL_SPI_Receive call fails.
If I comment out the following code in SPI_EndRxTransaction, the read succeeds.
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);
}
/* Erratasheet: BSY bit may stay high at the end of a data transfer in Slave mode */
if (hspi->Init.Mode == SPI_MODE_MASTER)
{
if (hspi->Init.Direction != SPI_DIRECTION_2LINES_RXONLY)
{
/* 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;
}
}
// COMMENT OUT THIS BLOCK AND THE SPI RECEIVE WORKS
// else
// {
// /* Wait the RXNE reset */
// if (SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_RXNE, RESET, Timeout, Tickstart) != HAL_OK)
// {
// SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
// return HAL_TIMEOUT;
// }
// }
// END COMMENTED BLOCK
}
else
{
/* Wait the RXNE reset */
if (SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_RXNE, RESET, Timeout, Tickstart) != HAL_OK)
{
SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
return HAL_TIMEOUT;
}
}
return HAL_OK;
}
Can someone please explain what I'm doing wrong? Thank You!
2021-05-25 07:33 PM
Save yourself the headache and use regular 2 line mode with the MOSI pin uninitialized. Receive only master has an awkward hardware implementation.
SPI_DIRECTION_2LINES_RXONLY doesn't get any use in the F4 CubeMX examples. I wouldn't be surprised if it didn't work.
2021-05-25 11:25 PM
The Rx-only SPI mode (and bidirectional mode in Rx) runs clocks continuously until SPI is disabled. As described in RM, this disable must happen *during* reception of the last intended frame not *after* it, as it is written in Cube. So, in Cube, as the disable comes late, the next frame starts to be received.
Note that the test for RXNE is outright wrong there. If baudate is low and/or compiler optimizations are enabled, the test will happen before that extraneous frame is received, and the RXNE flash set afterwards will cause problems when starting receiving the next frame. In the other case -as happened here - the extra frame is received completely and RXNE is set at time of test, then code waits until timeout (potentially forever if set so), as there's of course nothing which would clear RXNE in that waiting loop.
Remedy for this particular case is, as TDK said, to use the normal fullduplex mode without assigning a MOSI pin in GPIO.
The real solution of course would be to fix this in Cube for cases where Rx-only and more importantly, bidir, is genuinely needed, but apparently nobody cares.
JW
2021-05-26 12:03 PM
Thank you @TDK and @Community member!
Yes, I see the free running clock on my scope when using hspi3.Init.Direction = SPI_DIRECTION_2LINES_RXONLY. It makes sense that the receive buffer would have unintended data from these extra trailing clocks.
If I instead change the initialization code to simply hspi3.Init.Direction = SPI_DIRECTION_2LINES the acquisition succeeds.
One more question re. proper configuration. In MX Device Configuration, SPI3 is quite limited due to other MCU pins already assigned. I cannot choose Full-Duplex master. What is the correct way to circumvent this limitation, since I have no need for MOSI? I was hoping to keep things compatible with cube so that future automatic code generation doesn't clobber any tweaks I need to make for this problem.
Thanks again!
2021-05-26 12:07 PM
2021-05-28 01:27 PM
Thanks @TDK,
What I ended up doing was just modifying MX_SPI3_Init() like so:
static void MX_SPI3_Init(void)
{
/* USER CODE BEGIN SPI3_Init 0 */
/* USER CODE END SPI3_Init 0 */
/* USER CODE BEGIN SPI3_Init 1 */
/* USER CODE END SPI3_Init 1 */
/* SPI3 parameter configuration*/
hspi3.Instance = SPI3;
hspi3.Init.Mode = SPI_MODE_MASTER;
//hspi3.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
hspi3.Init.DataSize = SPI_DATASIZE_16BIT;
hspi3.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi3.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi3.Init.NSS = SPI_NSS_SOFT;
hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi3.Init.TIMode = SPI_TIMODE_DISABLE;
hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi3.Init.CRCPolynomial = 10;
//if (HAL_SPI_Init(&hspi3) != HAL_OK)
//{
// Error_Handler();
//}
/* USER CODE BEGIN SPI3_Init 2 */
/*
* We must configure SPI_DIRECTION_2LINES_RXONLY in MX due to pin limitations.
* This has a strange implementation in ST's HAL code. See the following.
* https://community.st.com/s/question/0D53W00000pT7bPSAS/spi-receive-times-out-due-to-spiflagrxne-not-reseting-in-spiendrxtransaction
* By switching to SPI_DIRECTION_2LINES here, we can use the default HAL code without modification.
*/
hspi3.Init.Direction = SPI_DIRECTION_2LINES;
// Optional: comment out this same code above USER CODE BEGIN SPI3_Init 2
if (HAL_SPI_Init(&hspi3) != HAL_OK)
{
Error_Handler();
}
/* USER CODE END SPI3_Init 2 */
}