2022-11-02 05:04 AM
Lets say i call HAL_SPI_TransmitReceive_DMA() and then I get an error or I decide to timeout while waiting for SPI data. What is the correct way to recover from an SPI error or a timeout?
I use this code and it seems to work:
// De-init SPI2
HAL_SPI_DeInit(&hspi2);
// Reset SPI2
__HAL_RCC_SPI2_FORCE_RESET();
__NOP();
__NOP();
__HAL_RCC_SPI2_RELEASE_RESET();
__NOP();
__NOP();
// Reset DMA1
__HAL_RCC_DMA1_FORCE_RESET();
__NOP();
__NOP();
__HAL_RCC_DMA1_RELEASE_RESET();
__NOP();
__NOP();
// Re-init DMA and SPI
MX_DMA_Init();
MX_SPI2_Init();
Solved! Go to Solution.
2022-11-03 02:18 AM
This is for STM32L152RE.
I came up with this solution.
__disable_irq();
HAL_SPI_DeInit(&hspi2);
__HAL_RCC_SPI2_FORCE_RESET();
__NOP();
__NOP();
__HAL_RCC_SPI2_RELEASE_RESET();
__NOP();
__NOP();
hspi2.hdmarx->Instance->CCR = 0;
hspi2.hdmarx->Instance->CMAR = 0;
hspi2.hdmarx->Instance->CNDTR = 0;
hspi2.hdmarx->Instance->CPAR = 0;
hspi2.hdmatx->Instance->CCR = 0;
hspi2.hdmatx->Instance->CMAR = 0;
hspi2.hdmatx->Instance->CNDTR = 0;
hspi2.hdmatx->Instance->CPAR = 0;
MX_SPI2_Init();
__enable_irq();
I had to include a forced reset on the SPI after HAL_SPI_DeInit() for it to work or else the SPI data sent on the next transfer was out of sync. And also reset the DMA registers. But the DMA 1 reset I was able to skip.
2022-11-02 07:48 AM
Which STM32?
Resetting SPI seems excessive to me, and resetting DMA even harmful if you have other peripherals using the same DMA too. Should be enough to disable/reenable SPI and correctly (following description in RM) stop/restart given DMA channel/stream.
You may need to reinitialize Cube state variables/structs, too. I don't use Cube.
JW
2022-11-03 02:18 AM
This is for STM32L152RE.
I came up with this solution.
__disable_irq();
HAL_SPI_DeInit(&hspi2);
__HAL_RCC_SPI2_FORCE_RESET();
__NOP();
__NOP();
__HAL_RCC_SPI2_RELEASE_RESET();
__NOP();
__NOP();
hspi2.hdmarx->Instance->CCR = 0;
hspi2.hdmarx->Instance->CMAR = 0;
hspi2.hdmarx->Instance->CNDTR = 0;
hspi2.hdmarx->Instance->CPAR = 0;
hspi2.hdmatx->Instance->CCR = 0;
hspi2.hdmatx->Instance->CMAR = 0;
hspi2.hdmatx->Instance->CNDTR = 0;
hspi2.hdmatx->Instance->CPAR = 0;
MX_SPI2_Init();
__enable_irq();
I had to include a forced reset on the SPI after HAL_SPI_DeInit() for it to work or else the SPI data sent on the next transfer was out of sync. And also reset the DMA registers. But the DMA 1 reset I was able to skip.
2023-08-29 04:37 AM
There was a bug in the previous solution.
I had to move HAL_SPI_DeInit(&hspi2) after the SPI have been reset or else the MISO pin would be pulled low for a short while.
This is the correct way:
__disable_irq();
__HAL_RCC_SPI2_FORCE_RESET();
__NOP();
__NOP();
__HAL_RCC_SPI2_RELEASE_RESET();
__NOP();
__NOP();
hspi2.hdmarx->Instance->CCR = 0;
hspi2.hdmarx->Instance->CMAR = 0;
hspi2.hdmarx->Instance->CNDTR = 0;
hspi2.hdmarx->Instance->CPAR = 0;
hspi2.hdmatx->Instance->CCR = 0;
hspi2.hdmatx->Instance->CMAR = 0;
hspi2.hdmatx->Instance->CNDTR = 0;
hspi2.hdmatx->Instance->CPAR = 0;
HAL_SPI_DeInit(&hspi2); // Note: It shall be here
MX_SPI2_Init();
__enable_irq();