cancel
Showing results for 
Search instead for 
Did you mean: 

How to setup SPI DMA on the M4 core for the STM32H745 chip

ConfusedContrarian
Associate III

I have a hobby project where I'm looking to design a flight controller using the STM32H745 microcontroller. My plan is to use the M4 core for data acquisition while using the M7 for the flight control stuff. I have a custom PCB with sensors connected over SPI, I2C and UART. and I'm trying to setup DMA for all these peripherals.

However, safe to say this MCU is a lot more complicated than previous things I've worked with. I've setup DMA for UART and that works fine. But I can't seem to get SPI DMA working at the moment on the M4. I've written a driver using the M7 core without DMA and that works fine but I've been unsuccessful so far on the other chip.

I just had a few questions to help me clarify a few things:

  • Are there cache coherence issues when using the M4 core? I know you can workaround this on the M7 core by setting your MPU regions or using SCB_clean and invalidate operations. But I don't see a data cache on the M4 chip. Is there a need to do this?
  • in terms of setting up shared memory regions when I want to move data from the M4 to the M7, is there a way to set up the memory regions in the linker script where both cores can easily access the same memory region?
  • Back to my original question, could someone please tell me what I'm doing wrong here?

My Sensor is connected to SPI1: and this is my init function generated by Cube

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_4BIT;
  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 = 0x0;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
  hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
  hspi1.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi1.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
  hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
  hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
  hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;
  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};
  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_HIGH;
    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_HIGH;
    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 */
  }
}
 
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{
 
  if(spiHandle->Instance==SPI1)
  {
  /* USER CODE BEGIN SPI1_MspDeInit 0 */
 
  /* USER CODE END SPI1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_SPI1_CLK_DISABLE();
 
    /**SPI1 GPIO Configuration
    PA5     ------> SPI1_SCK
    PA6     ------> SPI1_MISO
    PA7     ------> SPI1_MOSI
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7);
 
    /* SPI1 DMA DeInit */
    HAL_DMA_DeInit(spiHandle->hdmarx);
    HAL_DMA_DeInit(spiHandle->hdmatx);
 
    /* SPI1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(SPI1_IRQn);
  /* USER CODE BEGIN SPI1_MspDeInit 1 */
 
  /* USER CODE END SPI1_MspDeInit 1 */
  }
}

My NCS pin is on PA4 which is initialised in the gpio.c file. I then do the following in the main.c file to see if I can get the chip ID.

int main()
{
   MX_GPIO_Init();
   MX_DMA_Init();
   MX_SPI1_Init();
   HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
   uint8_t SerialData[3];
   SerialData[0] = 0xD0 | 0x80;
   uint8_t aRxBuffer[3]= {0};
   HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
   HAL_SPI_TransmitReceive_DMA(&hspi1, SerialData,  (uint8_t*)aRxBuffer, 3);
   HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
 
  while (1)
  {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }

I put a breakpoint in

void  HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
 
}

and the function gets called but I don't see any data in my buffer. I've tried all variants of the HAL_SPI_Transmit_DMA and Receive functions but no no avail. But this doesn't happen when I run ths on the M7.

I've opened a support ticket as I've been at this for two weeks now but I've not gotten anything back. I'd appreciate if anyone could help me out with this.

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru

The CM4 doesn't have cache so you don't have to worry about that. You still need to set variables volatile if they're modified by interrupts, or populated by DMA.

The CM4 and CM7 core can both access the SRAM1-4 memories. Put a pointer to a shared structure in there and transfer information that way. You'll need to manage cache on the CM7 side.

> HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);

> HAL_SPI_TransmitReceive_DMA(&hspi1, SerialData, (uint8_t*)aRxBuffer, 3);

> HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);

If PA4 is your CS line, this will raise CS prior to the transfer being complete. You should raise PA4 in the transfer complete callback instead.

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

View solution in original post

3 REPLIES 3
TDK
Guru

The CM4 doesn't have cache so you don't have to worry about that. You still need to set variables volatile if they're modified by interrupts, or populated by DMA.

The CM4 and CM7 core can both access the SRAM1-4 memories. Put a pointer to a shared structure in there and transfer information that way. You'll need to manage cache on the CM7 side.

> HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);

> HAL_SPI_TransmitReceive_DMA(&hspi1, SerialData, (uint8_t*)aRxBuffer, 3);

> HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);

If PA4 is your CS line, this will raise CS prior to the transfer being complete. You should raise PA4 in the transfer complete callback instead.

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

That did the trick, thanks! Still not sure why it worked on the M7 core though.

Hi! @DB.7abatunde​ 

Could you show your final code for SPI DMA initialisation ?

I want to send data from M4 core (stm32H745), but don't have any result

My topic with my code https://community.st.com/s/question/0D53W00001atD9zSAE/stm32h7-spi-dma-not-working

Best regards