(Solved) Unable to reset SPI busy flag in slave mode by any means
The solution was to hard reset the SPI module using RCC_APB1RSTR register.
HAL includes macros for this:
__HAL_RCC_SPI2_FORCE_RESET();
__HAL_RCC_SPI2_RELEASE_RESET();With these it completely resets and can be initialized again!
I'm using SPI slave mode. STM32F411. Tried with a 32F103CB and does the same thing.
Everything works fine if the communication is perfect, but I need to be able to recover from errors that could appear, ex. missing a clock pulse or some random noise.
When a certain time passes, if the SPI module is still busy, it means that it missed some clock pulses and it's still waiting for the rest.
This condition requires resetting the spi shift register, so the next frame is read correctly.
I checked the errata sheet, states that the BSY flag can get stuck in high state and suggests to use RXNE instead.
From 1st to 7th clock pulses the only flag that changes is BSY, nothing else changes in SR, CR1 and CR2 registers.
So that's the only way I have to know if the the SPI lost clocks and needs reset!
After the 8th clock BSY resets itself, always. And this is the only way to reset it.
But it's completely impossible to reset BSY when it got stuck in the middle of a transfer.
I tried this without success:
- Turn SPE bit off with __HAL_SPI_DISABLE(hspi). Disables SPI but clears nothing.
- Turn SPI clock off with __HAL_RCC_SPIx_CLK_DISABLE(). The registers go to 0, but restore the values when re-enabling the clock.
- Using HAL_SPI_Abort(). The function hangs for 8mS waiting for end of transaction, which will never happen because we have no more clocks! Hits the timeout and returns error.
- Msp Deinit / SPI Handle reset / Mx SPI Init / SPI start. Even wiping hspi2. The BSY remains.
HAL_SPI_MspDeInit(&hspi2);
__HAL_SPI_RESET_HANDLE_STATE(&hspi2);
memset((uint8_t*)&hspi2, 0,sizeof(hspi2));
MX_SPI2_Init();
HAL_SPI_Receive_IT(&hspi2, spiData, 2);With other MCUs I used in the past this was as easy as turning off the peripheral, clearing the flags and enabling it again.
I've been fighting with this issue a whole day. Am I missing something?
I've seen some similar posts here and in other forums, no one had a solution for this.
For now the only and dirty method was to use an external IO to pump the clock until the BSY is cleared:
// FIX_pin is joined with SCK.
// It's always an input, unless BSY bug appears, then is set as output until the problem is fixed.
while(SPI2->SR & SPI_SR_BSY){
HAL_GPIO_WritePin(FIX_GPIO_Port, FIX_Pin, 1);
asm("nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop");
HAL_GPIO_WritePin(FIX_GPIO_Port, FIX_Pin, 0);
asm("nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop");
}