I had a lot of problems with spi slave modes.
Any noise or glitch will start a transaction, hanging the spi peripheral into busy state.
Before calling hal transmit, check BSY bit in SR. If busy, it means it received at least one clock, and it's waiting more.
That means you lost clocks or got noise in the clock line. Reset the spi hw and re-init.
I tried everything to reset the SPI, nothing would clear the Busy flag.
After 3 days tearing my hair apart, someone suggested using RCC Reset register (Didn't knew it existed), worked like a charm.
https://community.st.com/s/question/0D53W00000gfktLSAQ/solved-unable-to-reset-spi-busy-flag-in-slave-mode-by-any-meansYou also need more checks:
- Ensure clock polarity and phase are correct.
- Check that the return of Hal transmit is HAL_OK.
- Is the 50mS timeout enough?
- NSS must be high when starting the HAL transfer, and go low before starting the clocks.
If you start the SPI transmit and it's already low, it will fail (at least in my tests).
Also, you're using polling mode, so all is done by software. 10MHz will send a byte every 0.8uS.
The MCU must be able to check the flags, load the data, and get everything ready in that time.
If you run the spi too fast in non DMA mode, it will likely cause buffer underrun.
Lower the speed to few KHz and try again, to discard too high speed.
If you are using only one spi device, you can get rid of the NSS pin.
I use this code to ensure the SPI is ready before receiving/transmitting, and reset the peripheral if not ready.
NSS is not used in my case (one 1 master, 1 slave)
I set a 1.5mS timer that will reset the SPI if no clock is received in that time (Once a frame starts, max time between bytes is 1mS).
Did my own ISR, so every byte received I reset the timer.
Once the timer overflows, it restores the SPI to receiving state, also checks the BSY Flag and if so, completely resets the SPI.
It works flawless. RCC Reset was the only way to completely reset the SPI H/W no matter its state.
if((hspi1.State>HAL_SPI_STATE_READY) && (hspi1.State<HAL_SPI_STATE_ERROR)){ // Handler BUSY in any mode (But not error or reset);
hspi1.State=HAL_SPI_STATE_READY; // Force ready state
}
if((hspi1.Instance->SR & SPI_SR_BSY) || (hspi1.State!=HAL_SPI_STATE_READY)){ // If peripheral is actually busy or handler not ready
hspi1.State=HAL_SPI_STATE_RESET; // Force reset state (HAL_SPI_Init will fail if not in reset state)
__HAL_RCC_SPI1_FORCE_RESET(); // Reset SPI1
asm("nop\nnop\nnop\nnop"); // Wait few clocks just in case
while(hspi1.Instance->SR & SPI_SR_BSY); // Wait until Busy is gone
__HAL_RCC_SPI1_RELEASE_RESET(); // Release reset
asm("nop\nnop\nnop\nnop"); // Wait few clocks just in case
while(hspi1.Instance->SR & SPI_SR_BSY); // Check again
if (HAL_SPI_Init(&hspi1) != HAL_OK){ Error_Handler(); } // Re-init SPI
}