cancel
Showing results for 
Search instead for 
Did you mean: 

Problem Restarting full duplex SPI DMA on STM32G474

SRond
Associate III

Good morning everyone,

I have an application where a STM32G474 as SPI master interacts with another ST controller as SPI slave. These are full-duplex DMA transfers of about 500 bytes at 10MBit transfer rate. The slave signals that it is alive via a digital output and then communication starts. This also works stably so far. However, if the slave loses its "life signal" once communication has started, either because it has made a mistake itself or because the data is faulty due to coupling and the whole thing has to be resynchronized, then the SPI-DMA does not restart.

I can understand that the master goes into the expected states to reset the SPI and then restart, but there is no physical restart. The clock signal and also MOSI remain permanently low after the transfer has been stopped. I'm probably just missing a little something to restart the SPI, but I can't figure it out at the moment. Maybe someone here has an idea where my error is.

Below is my initialization code and how I reset the SPI. Initialization is pretty much Cube standard. Thank you very much for your support.

 

 

void MX_SPI1_Init(void)
{

  /* USER CODE BEGIN SPI1_Init 0 */

  /* USER CODE END SPI1_Init 0 */

  /* USER CODE BEGIN SPI1_Init 1 */

  /* USER CODE END SPI1_Init 1 */
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 7;
  hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI1_Init 2 */

  /* USER CODE END SPI1_Init 2 */

}
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(spiHandle->Instance==SPI1)
  {
  /* USER CODE BEGIN SPI1_MspInit 0 */

  /* USER CODE END SPI1_MspInit 0 */
    /* SPI1 clock enable */
    __HAL_RCC_SPI1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**SPI1 GPIO Configuration
    PA5     ------> SPI1_SCK
    PA6     ------> SPI1_MISO
    PA7     ------> SPI1_MOSI
    */
    GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* SPI1 DMA Init */
    /* SPI1_RX Init */
    hdma_spi1_rx.Instance = DMA1_Channel4;
    hdma_spi1_rx.Init.Request = DMA_REQUEST_SPI1_RX;
    hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_spi1_rx.Init.Mode = DMA_NORMAL;
    hdma_spi1_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(spiHandle,hdmarx,hdma_spi1_rx);

    /* SPI1_TX Init */
    hdma_spi1_tx.Instance = DMA1_Channel5;
    hdma_spi1_tx.Init.Request = DMA_REQUEST_SPI1_TX;
    hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_spi1_tx.Init.Mode = DMA_NORMAL;
    hdma_spi1_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(spiHandle,hdmatx,hdma_spi1_tx);

  /* USER CODE BEGIN SPI1_MspInit 1 */

  /* USER CODE END SPI1_MspInit 1 */
  }
}

 

 

The code for resetting and also initially starting the SPI on the master. The counter is used to ensure that the SPI DMA, which may still be running, actually runs "empty". This should not actually be necessary as I abort and reset the SPI. It is also irrelevant for the behavior whether I wait for the counter to reset or whether I reset directly when the live signal disappears. Clock signal and MOSI are not restarted and without clock the slave logically does not send any data.

 

 

u16SPICounter++;
	if ((LL_GPIO_ReadInputPort(SPI1_INTERN_CS_GPIO_Port) & SPI1_INTERN_CS_Pin) == SPI1_INTERN_CS_Pin) {
		HAL_SPI_TransmitReceive_DMA(&hspi1, ((uint8_t*) u8SPIInternTXArr),
				((uint8_t*) u8SPIInternRXArr), (MAX_PARAMS * 2));
		u16SPICounter = 0;
		LL_GPIO_ResetOutputPin(TEST_PIN1_DO_GPIO_Port, TEST_PIN1_DO_Pin);
	} else {
		if(u16SPICounter == 50){
			HAL_SPI_Abort_IT(&hspi1);
			__HAL_RCC_SPI1_FORCE_RESET();
			__HAL_RCC_SPI1_RELEASE_RESET();
			LL_GPIO_SetOutputPin(TEST_PIN1_DO_GPIO_Port, TEST_PIN1_DO_Pin);
		}
	}

 

 

Thanks in Advance for your help.

Regards Sven

8 REPLIES 8
tjaekel
Senior III

Just my five cents...

I assume, your SPI slave does not "restart" anymore (actually, it does not finish the transfer).

Do you know which part (master or slave) has an issue to start again?

The master should actually start again, but possible that the slave never finishes its transaction, for these reasons:

  • the SPI slave sees a nCS signal de-asserted (going high again), before the expected number of bytes was transferred (caused by "cross-talk" between wires)
  • the slave is still waiting for some more clocks, e.g. a clock edge was missing
  • or too many clock cycles seen (due to "cross-talk") and the last byte (8bits) are not complete

I have seen also issues on STM MCUs as:

If you tie down the master SCLK signal (e.g. external driver pulls it down) - even the master does not clock out anymore data.

I suggest:

  • make sure, you do not have a signal issue:
    "cross-talks", e.g. SCLK toggles nCS signal, SCLK signal has "glitches":
    use a short cable, use a ribbon cable where every second is a GND wire (separation and shielding)
  • check, which device "stalls" (master or slave):
    if the right number of expected bits (N times 8bits (as bytes)) was there: if just one bit missing - the DMA will never complete

You can (SW) reset the SPI device(s), you can de-initialize and initialize again but it will not solve signal issues (if causing this problem, e.g. cross-talk).

Check also, if you use HW nCS signal or not (e.g. a GPIO pin as nCS, setting it before you start SPI). Or if SPI slave is using a HW nCS signal or if it enabled for SW slave enable:

If the start of a SPI transaction starts wrong, e.g. nCS is set low but the SCLK signal is not yet stable - a SPI device can see a "wrong" clock edge.

Use an oscilloscope to capture this issue and check the waveform.

When it comes to cross-talk:

  • check (and play) with the drive strength, the slew rate ("speed"):
    the faster the signals toggle - the more cross-talk (which can cause strange issues, esp. if "glitches" are seen (by the slave) on nCS and SCLK signal

It sounds more like an external signal issue to me (use a nice, "shielded", short cable). Check your GND connections (e.g. use a thick wire to ground both boards properly).

@tjaekelThanks for your answer.

The Master will not restart. Or at least the master does not start the clock again and therefore as you mentioned will not start driving the MOSI lane. I have currently pulled down the clock to ground. Since this is no initial issue because the communication over SPI works fine in the beginning I have not thought about possibly pulling it up but I will try it.

I already have played a bit with speed. I was down to around 500kBit and it did not change anything.

I do not think it is a hardware issue on the lines since the signals look pretty good on oscilloscope and the data is correct until the point were the slave does not drive the alive signal annymore and the master stops communication after finishing the current DMA transmission. The initial communication can run for hours without problems. If the transmission shall be startet again after it did run the master is not able to start the clock-signal again so I might try to pull it up rather than pull it down.

TDK
Guru

> __HAL_RCC_SPI1_FORCE_RESET();
> __HAL_RCC_SPI1_RELEASE_RESET();

Don't do this--it resets the peripheral and desynchronizes the state machine. Or if you do, you will have to re-initialize it from the beginning.

Should work if you take those out, assuming your code logic actually calls HAL_SPI_TransmitReceive_DMA again. You didn't post enough code to verify this.

If you feel a post has answered your question, please click "Accept as Solution".
SRond
Associate III

HAL_SPI_TransmitReceive_DMA is beeing called or at least I can see with an LED that the code switches between the part were this function is called and the part were it is not called.

After removing

> __HAL_RCC_SPI1_FORCE_RESET();
> __HAL_RCC_SPI1_RELEASE_RESET();

I can see with an oscilloscope that the SPI does restart, but only for around 1 or 2 minutes and than it will not start again, even though I can see from the toggling LED that both cases, the one that calls the TransmitReceive and the other are both entered.

Tomorrow I am going to check if the compiler does something strange because I have some code optimizations activated.

Sounds like a code bug. Step through, examine why it's not re-starting. There is likely a lot more code logic that you're not including here that is critical to understanding what is going on. The chip is not prone to misbehavior--register values dictate how it acts and code dictates what those values are.

Unlikely to be a compiler issue.

If you feel a post has answered your question, please click "Accept as Solution".
SRond
Associate III

Yes, you are propably right with the code bug. I am not really believing in a compiler issue myself. This is more of a last hope before I start switching to something else for the communication between the two controllers 😉

Regarding the code: this is everything I do with SPI1 but there is a lot going on with DMA because I use DMA for a lot of ADC´s as well. But if it is a DMA issue rather than something with the SPI call it should happen during the times when the communication is running stbale for hours as well.

In another internet group someone mentioned that the BSY flag is not beeing reset sometimes when it comes to errors so I am going to check that as well.

Just as a caution: There are a lot fewer issues with the silicon than forums would let you believe. And the limitations that do exist are typically captured in the errata sheet, especially on older and popular chips such as this one.

Typically there is no shortcut to solve code bugs.

If you feel a post has answered your question, please click "Accept as Solution".
SRond
Associate III

This is not a solution for everybody but since nothing else worked I switched master and slave.