cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 HAL SPI DMA Not receiving data

Rob Ashworth
Associate III

I'm having a problem using DMA with SPI.

Fault is almost certainly me as this is the first time I have used SPI/DMA.  Normally I just wait for SPI to finish, but on this occasion it is taking too long so I need to move the process away from the CPU waiting.

When I use normal SPI_TransmitReceive it is all ok so not suspecting a hardware problem.  I'm not familiar with DMA at all, so not sure whether to use FIFO, circular buffers or how to allow enough space for the 27 Byte transfer.

RxBuffer is always 0x00...

Receive/Tx:

/Read all 8 channels
void SPI_ADC_READ(uint8_t ADCn)
{

   uint8_t Cmd[27] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
   //HAL_SPI_TransmitReceive(&ADC_SPI, Cmd, ADC_DATA, 27, 1000);
//   HAL_SPI_TransmitReceive(&ADC_SPI, Cmd, ADS1298RData.ByteValue, 27, 1000);
//   counter1++;
  // HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size);
   HAL_SPI_TransmitReceive_DMA(&ADC_SPI, Cmd, RxBuffer, 27);
   counter1++;
}

SPI/DMA config

/* SPI3 init function */
void MX_SPI_ADC_Init(void)
{
  /* SPI3 parameter configuration*/
  ADC_SPI.Instance = SPI4;
  ADC_SPI.Init.Mode = SPI_MODE_MASTER;
  ADC_SPI.Init.Direction = SPI_DIRECTION_2LINES;
  ADC_SPI.Init.DataSize = SPI_DATASIZE_8BIT;
  ADC_SPI.Init.CLKPolarity = SPI_POLARITY_LOW;
  ADC_SPI.Init.CLKPhase = SPI_PHASE_2EDGE;//1EDGE or 2
  ADC_SPI.Init.NSS = SPI_NSS_SOFT;
  ADC_SPI.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  ADC_SPI.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;//2 is 20Mhz which is flat out for the ADC
  ADC_SPI.Init.FirstBit = SPI_FIRSTBIT_MSB;
  ADC_SPI.Init.TIMode = SPI_TIMODE_DISABLE;
  ADC_SPI.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  ADC_SPI.Init.CRCPolynomial = 10;
  ADC_SPI.Init.MasterKeepIOState=SPI_MASTER_KEEP_IO_STATE_ENABLE;
  //New for DMA
  ADC_SPI.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;

  //Configure the Peripheral
  HAL_SPI_Init(&ADC_SPI);


   /* Configure the DMA handler for Transmission process */
  hdma_spi4_rx.Instance = DMA1_Stream0;
  hdma_spi4_rx.Init.Request = DMA_REQUEST_SPI4_RX;
  hdma_spi4_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
  hdma_spi4_rx.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_spi4_rx.Init.MemInc = DMA_MINC_ENABLE;
  hdma_spi4_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  hdma_spi4_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma_spi4_rx.Init.Mode = DMA_NORMAL;
  hdma_spi4_rx.Init.Priority = DMA_PRIORITY_LOW;
  hdma_spi4_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

  HAL_DMA_Init(&hdma_spi4_rx);

  __HAL_LINKDMA(&ADC_SPI,hdmarx,hdma_spi4_rx);

  /* SPI4 interrupt Init */

  HAL_NVIC_SetPriority(SPI4_DMA_TX_IRQn, 1, 1);
   HAL_NVIC_EnableIRQ(SPI4_DMA_TX_IRQn);

   /* NVIC configuration for DMA transfer complete interrupt (SPI1_RX) */
   HAL_NVIC_SetPriority(SPI4_DMA_RX_IRQn, 1, 0);
   HAL_NVIC_EnableIRQ(SPI4_DMA_RX_IRQn);

   /*##-5- Configure the NVIC for SPI #########################################*/
   /* NVIC configuration for SPI transfer complete interrupt (SPI1) */
   HAL_NVIC_SetPriority(SPI4_IRQn, 1, 0);
   HAL_NVIC_EnableIRQ(SPI4_IRQn);

}

 

 

3 REPLIES 3
TDK
Guru

A few pitfalls here:

  • You are passing a stack parameter/buffer (Cmd) to a DMA function. DMA happens in the background, and the buffer must remain valid until the operation is complete. You're not waiting for completion, so that data goes out of scope and can get overwritten elsewhere. This can cause stack corruption.
  • Your program doesn't do anything with the data.
  • The H7 has a data cache. Cache must be disabled or handled appropriately.
  • The H7 DMA cannot access all memory. Ensure your stack is in a DMA accessible area.

See this for more information on the last two points:

https://community.st.com/t5/stm32-mcus/dma-is-not-working-on-stm32h7-devices/ta-p/49498

 

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

I'm still hopelessly stuck on this topic.

Played around with it for a few days now but not getting very far.

There doesn't seem to be much helpful documentation on what the SPI-DMA settings are doing within cube IDE, or how it is affecting the .ld file.

I'm relying on CubeIDE to configure the linker script correctly for me purposes...

Is there a very simple example for the H7 which might show how to read 27 bytes from the SPI bus when interrupted by a GPIO input. (The GPIO interrupt is working fine, but I can only read the SPI when the GPIO triggers)

/* USER CODE BEGIN 1 */
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
  wTransferState = TRANSFER_COMPLETE;
  /* Invalidate cache prior to access by CPU */
  SCB_InvalidateDCache_by_Addr ((uint32_t *)aRxBuffer, BUFFERSIZE);
  counter1++;
}

//Read all 8 channels
void SPI_ADC_DMA(void)
{
	   uint8_t Cmd[27] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	   HAL_SPI_TransmitReceive_DMA(&hspi1, Cmd,aRxBuffer, 27);
//	   counter1++;
//	   while (wTransferState == TRANSFER_WAIT)
//	   {
  //     }
}

 

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

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

  /* USER CODE END SPI1_MspInit 0 */

  /** Initializes the peripherals clock
  */
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI1;
    PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
    {
      Error_Handler();
    }

    /* 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_Stream0;
    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_LOW;
    hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    //
     //
    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_Stream1;
    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_LOW;
    hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(spiHandle,hdmatx,hdma_spi1_tx);

    /* SPI1 interrupt Init */
    HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(SPI1_IRQn);
  /* USER CODE BEGIN SPI1_MspInit 1 */

  /* USER CODE END SPI1_MspInit 1 */
  }
}

 

TDK
Guru

I would recommend disabling cache and getting started with blocking functions to get familiar with how the chip works. There are several SPI examples in the CubeMX repository, including ones that use DMA. You can use the Example Selector to open them.

TDK_0-1703251967782.png

 

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