0
\$\begingroup\$

On STM32H7 I'm using DMA for SPI transfer. I use SPI1 and SPI4, both in D2 domain, together with RAM_D2.

In the linker file I added:

.dma_buffer :
{
  . = ALIGN(4);

  *(.dma_buffer)

} >RAM_D2

The buffer is declared like:

uint8_t buf[2048] __attribute__ ((section(".dma_buffer"))) __attribute__ ((aligned (32)));

Transmitting using DMA works:

/* Mem-to-Peri */
SCB_CleanDCache_by_Addr((uint32_t*)(((uint32_t)buf) & ~(uint32_t)0x1F), len + 32);
errorcode = HAL_SPI_Transmit_DMA(interface->hspi, (uint8_t*) buf, len);
/* an interrupt will occur */
if (errorcode != HAL_OK)
{
  dmc_puts(__FILE__);
  dmc_puts(", ");
  dmc_putint(__LINE__);
  dmc_puts(": Error: HAL_SPI_Transmit");
}

There are no errors after calling HAL_SPI_Transmit_DMA, HAL_SPI_Receive_DMA, or without DMA.

Receiving data from SPI without using DMA also works, I get valid data in.

But receiving data from SPI using DMA, the buffer gets filled with 0's after cache invalidation:

/* Peri-to-Mem */
errorcode = HAL_SPI_Receive_DMA(interface->hspi, (uint8_t*) buf, len);

/* Some code to wait for the transfer to be finished */
wait_dma_rx_ended(interface);

SCB_InvalidateDCache_by_Addr((uint32_t*)(((uint32_t)buf) & ~(uint32_t)0x1F), ((len+31)/32)*32);

I also configured the MPU, but I think it is not needed when using the cache maintenance functions.

void MPU_Config(void)
{
  MPU_Region_InitTypeDef MPU_InitStruct = { 0 };

  /* Disable the MPU */
  HAL_MPU_Disable();

  /* Initialize and configure the Region and the memory to be protected */
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER0;
  MPU_InitStruct.BaseAddress = 0x30000000;
  MPU_InitStruct.Size = MPU_REGION_SIZE_8KB;
  MPU_InitStruct.SubRegionDisable = 0x0;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

  HAL_MPU_ConfigRegion(&MPU_InitStruct);

  /* Enable the MPU */
  HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

My SPI and DMA in initialized like this:

/**
 * @brief Initialize SPI bus
 * @param None
 * @return Error code
 */
uint8_t ksz8851_interface_init(struct KSZ8851_INTERFACE *interface)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  HAL_DMA_MuxSyncConfigTypeDef pSyncConfig= {0};

  dmc_puts("ksz8851_interface_init\n");

  /* Configure the SPI peripheral */
  /* Set the SPI parameters */
  if (interface->hspi->Instance == SPI1)
  {
    dmc_puts("SPI1...\n");
    __HAL_RCC_SPI1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();

    /**SPI1 GPIO Configuration
    PA4     ------> SPI1_CS
    PA5     ------> SPI1_SCK
    PA6     ------> SPI1_MISO
    PD7     ------> SPI1_MOSI
    */
    GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6;
    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);

    GPIO_InitStruct.Pin = 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(GPIOD, &GPIO_InitStruct);

    /* SPI1 DMA Init */
    /* SPI1_TX Init */
    hdma_spi1_tx.Instance = DMA1_Stream0;
    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_HIGH;
    hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
    {
      Error_Handler();
    }

    pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_EXTI0;
    pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_NO_EVENT;
    pSyncConfig.SyncEnable = DISABLE;
    pSyncConfig.EventEnable = DISABLE;
    pSyncConfig.RequestNumber = 1;
    if (HAL_DMAEx_ConfigMuxSync(&hdma_spi1_tx, &pSyncConfig) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(interface->hspi,hdmatx,hdma_spi1_tx);

    /* SPI1_RX Init */
    hdma_spi1_rx.Instance = DMA1_Stream1;
    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_HIGH;
    hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
    {
      Error_Handler();
    }
  }
  if (interface->hspi->Instance == SPI4)
  {
    dmc_puts("SPI4...\n");
    __HAL_RCC_SPI4_CLK_ENABLE();
    __HAL_RCC_GPIOE_CLK_ENABLE();
    /**SPI4 GPIO Configuration
    PE11     ------> SPI4_CS
    PE12     ------> SPI4_SCK
    PE13     ------> SPI4_MISO
    PE14     ------> SPI4_MOSI
    */
    GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14;
    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_SPI4;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

    /* SPI4 DMA Init */
    /* SPI4_RX Init */
    hdma_spi4_rx.Instance = DMA1_Stream2;
    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_HIGH;
    hdma_spi4_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    if (HAL_DMA_Init(&hdma_spi4_rx) != HAL_OK)
    {
      Error_Handler();
    }

    pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_EXTI0;
    pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_NO_EVENT;
    pSyncConfig.SyncEnable = DISABLE;
    pSyncConfig.EventEnable = DISABLE;
    pSyncConfig.RequestNumber = 1;
    if (HAL_DMAEx_ConfigMuxSync(&hdma_spi4_rx, &pSyncConfig) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(interface->hspi,hdmarx,hdma_spi4_rx);

    /* SPI4_TX Init */
    hdma_spi4_tx.Instance = DMA1_Stream3;
    hdma_spi4_tx.Init.Request = DMA_REQUEST_SPI4_TX;
    hdma_spi4_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_spi4_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi4_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi4_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_spi4_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_spi4_tx.Init.Mode = DMA_NORMAL;
    hdma_spi4_tx.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_spi4_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    if (HAL_DMA_Init(&hdma_spi4_tx) != HAL_OK)
    {
      Error_Handler();
    }
  }

  interface->hspi->Init.Mode = SPI_MODE_MASTER;
  interface->hspi->Init.Direction = SPI_DIRECTION_2LINES;
  interface->hspi->Init.DataSize = SPI_DATASIZE_8BIT;
  interface->hspi->Init.CLKPolarity = SPI_POLARITY_LOW;
  interface->hspi->Init.CLKPhase = SPI_PHASE_1EDGE;
  interface->hspi->Init.NSS = SPI_NSS_SOFT;  // SPI_NSS_HARD_OUTPUT
  // SPI4: 100MHz / 8 = 12.5 MHz: SPI_BAUDRATEPRESCALER_8
  interface->hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;  // KSZ8851SNL can handle up to 50 MHz. 256: 0.78125
  interface->hspi->Init.FirstBit = SPI_FIRSTBIT_MSB;
  interface->hspi->Init.TIMode = SPI_TIMODE_DISABLE;
  interface->hspi->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  interface->hspi->Init.CRCPolynomial = 7;
  interface->hspi->Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  interface->hspi->Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
  interface->hspi->Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
  interface->hspi->Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  interface->hspi->Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  interface->hspi->Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_03CYCLE;
  interface->hspi->Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_03CYCLE;
  interface->hspi->Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  interface->hspi->Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
  interface->hspi->Init.IOSwap = SPI_IO_SWAP_DISABLE;
  if (HAL_SPI_Init(interface->hspi) != HAL_OK)
  {
    return false;
  }

  /* Pull the CS pin low */
  HAL_GPIO_WritePin((GPIO_TypeDef*) interface->cs_port, interface->cs_pin, GPIO_PIN_RESET);

  return true;
}

So the problem is I do not receive valid data when using DMA, but I do get valid data when not using DMA. So I think the SPI is configured correctly for the device.

Using DMA, if I don't invalidate the cache, I see the same data in the buffer as before calling HAL_SPI_Receive_DMA, which is expected. But after cache invalidation (calling SCB_InvalidateDCache_by_Addr), I do read all 0's, instead of valid data, which is not expected.

Using DMA transmit SPI works just fine, as does SPI receive without DMA.

What can I do to troubleshoot the problem further?

Has anybody seen this behavior on STM32H7 MCU's?

\$\endgroup\$
  • \$\begingroup\$ Hi Jack, Can you post your HSPI initialization options. It's tough to tell how you have the SPI configured from what you have. Have you stepped through the code using a debugger? Is a value populated in errorcode when returning from HAL_SPI_Receive_DMA? These would be helpful to know. I'm glad you've already addressed the DMA domain and alignment things which tend to trip people up. \$\endgroup\$ – pgvoorhees Apr 23 at 20:23
  • \$\begingroup\$ Hi Peter, thanks for bearing with me. Yes, that's an important part too, I just added it in the original post as wel that there are no errors after calling HAL_SPI_Transmit_DMA, HAL_SPI_Receive_DMA, or without DMA. \$\endgroup\$ – Jack Apr 24 at 8:43
  • \$\begingroup\$ Hi Peter, I did not step through the code, yet. In my example there are two (SPI) KSZ8851SNL network interfaces, which are each connected to a MODBUS-TCP device, a master and a slave. Those chips use state machines and stop working when trying to debug like that. \$\endgroup\$ – Jack Apr 24 at 9:08
  • \$\begingroup\$ For the STM32H750 I've been trying to get I2S2 Master RX Full Duplex DMA to work (I have this working in STM32F302) but this chip does not seem to support that with the DMA. Beside CubeMX not having Rx/Tx DMA for this I2S, "SPI_I2S_FULLDUPLEX_SUPPORT" is not defined in the latest drivers. \$\endgroup\$ – user221997 May 14 at 23:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.