cancel
Showing results for 
Search instead for 
Did you mean: 

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

MSipo
Senior II

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

1 ACCEPTED SOLUTION

Accepted Solutions

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.

View solution in original post

3 REPLIES 3

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

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.

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