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
waclawek.jan
Super User

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