Skip to main content
MSipo
Senior II
November 2, 2022
Solved

What is the correct way to recover from an SPI error or a timeout?

  • November 2, 2022
  • 1 reply
  • 3206 views

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();

This topic has been closed for replies.
Best answer by MSipo

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.

1 reply

waclawek.jan
Super User
November 2, 2022

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

MSipo
MSipoAuthorBest answer
Senior II
November 3, 2022

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.

MSipo
MSipoAuthor
Senior II
August 29, 2023

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();