cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F733xx SPI DMA transfer issue.

Grundyoso
Associate II

I've built a custom system using the STM32F733xx and an external ADC (TI ADS8588S) connected over SPI. I have verified that the system works consistently using the STM32 HAL L0_V1.2.8 when transferring data via the blocking/polling method. However, when I configure the SPI to run non-blocking with a linked DMA I can only get one full transfer to complete. Successive transfers fail to produce SCLK as shown below:

0693W00000AOPZnQAP.pngThis is the SPI and DMA configuration code:

  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_2EDGE; // Needed for Saleae match
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
  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();
  }
 
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hspi->Instance==SPI1)
  {
  /* USER CODE BEGIN SPI1_MspInit 0 */
 
  /* USER CODE END SPI1_MspInit 0 */
    /* Peripheral 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_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
    /* SPI1_RX DMA Init */
    __HAL_RCC_DMA2_CLK_ENABLE();
    hdma_spi1_rx.Instance = DMA2_Stream2;
    hdma_spi1_rx.Init.Channel = DMA_CHANNEL_3;
    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_HALFWORD;
    hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_spi1_rx.Init.Mode = DMA_NORMAL;
    hdma_spi1_rx.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
 
    if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
    {
      Error_Handler();
    }
    HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0); // SPI1 RX - Chan 3
    HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
    __HAL_LINKDMA(hspi,hdmarx,hdma_spi1_rx); /*link DMA2 to SPI1 Rx*/
 
    /*DMA 1 / channel 0 / stream 5 sends data to SPI (no GPIO pin existent, only needed for clocking in right number of bytes)*/
    __HAL_RCC_DMA2_CLK_ENABLE(); /*DMA clock*/
    hdma_spi1_tx.Instance = DMA2_Stream3;
    hdma_spi1_tx.Init.Channel = DMA_CHANNEL_3;
    hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; /*from memory to SPI*/
    hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE; /*no increment on SPI side*/
    hdma_spi1_tx.Init.MemInc = DMA_MINC_DISABLE; /*no increment sample memory address*/
    hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_spi1_tx.Init.Mode = DMA_NORMAL;
    hdma_spi1_tx.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
    {
      Error_Handler();
    }
    __HAL_LINKDMA(hspi, hdmatx, hdma_spi1_tx); /*link DMA2 to SPI1 Tx*/
 
  /* USER CODE BEGIN SPI1_MspInit 1 */
 
  /* USER CODE END SPI1_MspInit 1 */
  }
}

I am calling for the DMA transfer on 2 lines (TX/RX) so I setup the TX line only to send out dummy data and generate SCLK:

static uint16_t ulDummyWord[16]={0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF};
static uint16_t Adc1RxBuff[16];
 
int main(void)
{
  /* USER CODE BEGIN 1 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_Delay(10);
    HAL_GPIO_WritePin(ADDevice1.AD_pinMapping->CS_gpioPort, ADDevice1.AD_pinMapping->CS_gpioPin, 0); // select ADC1 for SPI transfer
    HAL_SPI_TransmitReceive_DMA(hspi1,(uint8_t *)ulDummyWord,(uint8_t *)Adc1RxBuff, 16);  // this only works once
   }
}
 
void DMA2_Stream2_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream2_IRQn 0 */
 
  __HAL_DMA_CLEAR_FLAG(&hdma_spi1_rx,DMA_FLAG_TCIF2_6);
  __HAL_DMA_CLEAR_FLAG(&hdma_spi1_rx,DMA_FLAG_HTIF2_6);
  __HAL_DMA_CLEAR_FLAG(&hdma_spi1_tx,DMA_FLAG_TCIF3_7);
  __HAL_DMA_CLEAR_FLAG(&hdma_spi1_tx,DMA_FLAG_HTIF3_7);
  __HAL_DMA_CLEAR_FLAG(&hdma_spi1_tx,DMA_FLAG_FEIF3_7);
  HAL_GPIO_WritePin(ADDevice1.AD_pinMapping->CS_gpioPort, ADDevice1.AD_pinMapping->CS_gpioPin, 1);
}

The issue is that HAL_SPI_TransmitReceive_DMA call errors out on the 2nd call with HAL_BUSY because hspi->State is HAL_SPI_STATE_BUSY_TX_RX. I thought the root cause was related to SPI Tx sending dummy data and asserting the DMA_FLAG_FEIF3_7 flag but clearing this didn't help.

I cannot see how to reset the SPI1 DMA so that successive transfers can happen. I have suspicions on the status flags (TXE, RXNE) but I cannot see what is wrong.

Thank you in advance.

1 ACCEPTED SOLUTION

Accepted Solutions
Grundyoso
Associate II

I was able to get this to work (finally). There were several issues with my code, the most obvious being that I wasn't following the HAL design pattern so it's state wasn't properly being managed. Once I added " HAL_DMA_IRQHandler(hspi1.hdmarx);" in place of the code I had in "void DMA2_Stream2_IRQHandler(void)" and moved that code to "HAL_SPI_TxRxCpltCallback()" then things started working. I was daisy chaining the DMA transfers which was causing a problem as well so had to use a TIMER to schedule each subsequent transfer. Once all that was set, things worked as expected.

View solution in original post

6 REPLIES 6
TDK
Guru

> STM32F733xx 

> using the STM32 HAL L0_V1.2.8 

Typo? Or are you using L0 libraries on an F7 device?

Looking at the return value from HAL_SPI_TransmitReceive_DMA when it fails would probably shed some light.

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

Perhaps a typo... I'm using the STM32F7xx_HAL_Driver that was included with the STM32CubeIDE_1.4.0 ... I thought this was V1.2.8 of the HAL as listed in the GitHub repo https://github.com/STMicroelectronics/STM32CubeF7

The return value of the HAL_SPI_TransmitReceive_DMA when it fails is errorcode HAL_BUSY ... I think the HAL is stuck in the HAL_BUSY state because the SPI Tx is sending dummy data and perhaps doesn't get a signal that the transfer was completed (since the MOSI line isn't active)?? The only thing that puzzles me is what this is not an issue when I use HAL_SPI_TransmitReceive, which works fine.

Any ideas what could be going on? I've been stuck at this for a few days now :(

TDK
Guru

Looks like you've deleted the required HAL_DMA_IRQHandler call from within DMA2_Stream2_IRQHandler. You should only be adding code between USER BEGIN / USER END sections, and not deleting stuff outside of that.

You shouldn't be clearing flags, the HAL library does that and clearing them yourself will prevent the code from doing its thing.

If you want to access the end of the transfer, consider implementing HAL_SPI_TxRxCpltCallback instead to de-assert the CS line.

HAL_SPI_TransmitReceive works because it's a blocking call and doesn't need an interrupt to set the state back to ready.

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

(I corrected some typos in this post which may have been confusing in the original reply. Replying to this so you get another notification.)

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

I was able to get this to work (finally). There were several issues with my code, the most obvious being that I wasn't following the HAL design pattern so it's state wasn't properly being managed. Once I added " HAL_DMA_IRQHandler(hspi1.hdmarx);" in place of the code I had in "void DMA2_Stream2_IRQHandler(void)" and moved that code to "HAL_SPI_TxRxCpltCallback()" then things started working. I was daisy chaining the DMA transfers which was causing a problem as well so had to use a TIMER to schedule each subsequent transfer. Once all that was set, things worked as expected.

Hi, I wonder if clearing flags in interrupt callback is crucial? Thanks