cancel
Showing results for 
Search instead for 
Did you mean: 

SPI DMA STM32H742VI with LL with Polling

clonephone82
Associate III

Hello,

I have tried to implement a simple SPI driver.

Only with LL Lib. I would like to use DMA and I will do it without interrupts.

So it works now. But for me it is not clear how to poll if the transfer is finish in the right way.

So I use rx and tx dma therefore I taught the byte shifted in, guarantees the last shift out is done. That means i only wait for the rx dma stream to become ready.

So my dma transfer and init functions looks like that way

static void dma_init(void)
{
// dma init snippet
 /* DMA controller clock enable */
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
 
  /* SPI3_TX Init */
  LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_1, LL_DMAMUX1_REQ_SPI3_TX);
  LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_1, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
  LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_1, LL_DMA_PRIORITY_VERYHIGH);
  LL_DMA_SetMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MODE_NORMAL);
  LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_PERIPH_NOINCREMENT);
  LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MEMORY_INCREMENT);
  LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_1, LL_DMA_PDATAALIGN_BYTE);
  LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_1, LL_DMA_MDATAALIGN_BYTE);
  LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_1);
 
  /* SPI3_RX Init */
  LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_0, LL_DMAMUX1_REQ_SPI3_RX);
  LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_0, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
  LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_0, LL_DMA_PRIORITY_VERYHIGH);
  LL_DMA_SetMode(DMA1, LL_DMA_STREAM_0, LL_DMA_MODE_NORMAL);
  LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_0, LL_DMA_PERIPH_NOINCREMENT);
  LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_0, LL_DMA_MEMORY_INCREMENT);
  LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_0, LL_DMA_PDATAALIGN_BYTE);
  LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_0, LL_DMA_MDATAALIGN_BYTE);
  LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_0);
 
  LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_0, 0);
  LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, 0);
 
 
  /* SPI3 parameter configuration*/
  SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
  SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
  SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
  SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
  SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
  SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
  SPI_InitStruct.BaudRate = PLATFORM_NETX_STM32H_SPI_CLOCK;
  SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
  SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
  SPI_InitStruct.CRCPoly = 0x0;
 
  LL_SPI_EnableGPIOControl(SPI3);
  LL_SPI_SetStandard(SPI3, LL_SPI_PROTOCOL_MOTOROLA);
  LL_SPI_Init(SPI3, &SPI_InitStruct);
 
  LL_SPI_EnableDMAReq_RX(SPI3);
  LL_SPI_EnableDMAReq_TX(SPI3);
 
  LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_0);
  LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_1);
 
  /* LL_DMA_STREAM_0 => RX */
  LL_DMA_ConfigAddresses(DMA1,
                         LL_DMA_STREAM_0,
                         (uint32_t) &(SPI3->RXDR),
                         (uint32_t) receiveBuffer,
                         LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_STREAM_0));
 
  /* LL_DMA_STREAM_1 => TX */
  LL_DMA_ConfigAddresses(DMA1,
                         LL_DMA_STREAM_1,
                         (uint32_t)transmitBuffer,
                         (uint32_t) &(SPI3->TXDR),
                         LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_STREAM_1));
 
  /* clear flags */
  LL_DMA_ClearFlag_TC0(DMA1);
  LL_DMA_ClearFlag_TC1(DMA1);
 
  LL_DMA_ClearFlag_HT0(DMA1);
  LL_DMA_ClearFlag_HT1(DMA1);
 
  LL_DMA_ClearFlag_TE0(DMA1);
  LL_DMA_ClearFlag_TE1(DMA1);
 
  /* Enable SPI1 */
  LL_SPI_Enable(SPI3);
 
}
 
static void tranfsere_spi_dma(uint32_t ulLength)
{
  // disable DMA streams
  LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_0);
  LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_1);
 
  // disable spi needed on h7
  LL_SPI_Disable(SPI3);
 
  LL_DMA_ClearFlag_TC0(DMA1);
  LL_DMA_ClearFlag_TC1(DMA1);
  LL_DMA_ClearFlag_HT0(DMA1);
  LL_DMA_ClearFlag_HT1(DMA1);
  LL_DMA_ClearFlag_FE0(DMA1);
  LL_DMA_ClearFlag_FE1(DMA1);
 
  LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_0, ulLength);
  LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, ulLength);
 
  // Enable DMA Channels
  LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_0);
  LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_1);
 
  // Enable SPI and start master transfer
  LL_SPI_Enable(SPI3);
  LL_SPI_StartMasterTransfer(SPI3);
 
  while (!LL_DMA_IsActiveFlag_TC0(DMA1))
  {
    // todo error handling
  }
}

So is it enough to wait only for TC0 active ?

Is it better to use an Interrupt in relation to performance?

Do I need caching in any way? My buffers are on 0x24xxxxxx addresses.

By the way is there an example for H7 with DMA and LL ??

many thanks

cheers mathias

4 REPLIES 4
TDK
Guru

TC is set when the last data is transferred by the DMA to the SPI TXFIFO, but that doesn't mean the SPI is done. You would need to wait until it's done sending the data.

One way of doing this is by setting TSIZE appropriately and monitoring for EOT bit to be set, which is what HAL does.

https://github.com/STMicroelectronics/STM32CubeH7/blob/master/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_spi.c#L978

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

Hi TDK,

Many thanks for your answer. I did not find the where they set TSIZE.

Oh sorry i didn´t seen that.

/* Set the number of data at current transfer */

MODIFY_REG(hspi->Instance->CR2, SPI_CR2_TSIZE, Size);

So but what I do not understand I use spi with dma and I taught the dma controlls the spi.

I should also set the TSIZE?

  LL_DMA_ClearFlag_FE1(DMA1);
 
  LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_0, ulLength);
  LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, ulLength);
 
//????
  /* Set the number of data at current transfer */
  LL_SPI_SetTransferSize(SPI3, ulLength);
 
  // Enable DMA Channels
  LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_0);

But why I do not found that in any example code.

I don't know what example code you're referring to. Read the RM if you want the intimate details of how the complicated SPI peripheral works on the H7. The peripheral has multiple modes of operation and TSIZE is not required to be used.

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