Skip to main content
Graduate II
January 30, 2024
Question

Problem Restarting full duplex SPI DMA on STM32G474

  • January 30, 2024
  • 3 replies
  • 3777 views

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

    This topic has been closed for replies.

    3 replies

    Visitor II
    January 30, 2024

    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).

    SRondAuthor
    Graduate II
    January 30, 2024

    @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.

    Super User
    January 30, 2024

    > __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.

    SRondAuthor
    Graduate II
    January 30, 2024

    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.

    Super User
    January 30, 2024

    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.

    SRondAuthor
    Graduate II
    February 7, 2024

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