cancel
Showing results for 
Search instead for 
Did you mean: 

SPI DMA transfer issue on STM32H743

oseaw.1
Associate II

 

I am reaching out to seek your expertise regarding the DMA transfer issue on SPI that I encountered while using the STM32H743 microcontroller.

When an EXTI LINE 0 event (data ready) occurs, attached to the code is a program in which 30 bytes of 0xFF bytes are transmitted to the DMA Tx buffer, and then 30 bytes are read through the DMA Rx buffer.

The problem is that data is not received properly in the DMA receive buffer. Data continues to circulate at the 7th and 21st indices of the receiving buffer. Except for the two indexes, the remaining buffer space is not filled with anything.

I placed the DMA Rx and Tx buffer to D2 RAM and disabled the data cache of the region. And, Even if I add some delay time before starting the DMA read in Line 7 of EXTI0_IRQHandler(), the data transfer problem still occurs.

However, it seems that the DMA transfer is working normally after stopping and resuming by the breakpoint on Line 7 of EXTI0_IRQHandler().

Please give me advice on which part I should look into more.


Disable D2 RAM region as non cacheable

 

    MPU_Region_InitTypeDef MPU_RegionTypeDef;

    HAL_MPU_Disable();    

    MPU_RegionTypeDef.Enable = MPU_REGION_ENABLE;
    MPU_RegionTypeDef.Number = MPU_REGION_NUMBER1;
    MPU_RegionTypeDef.BaseAddress = 0x30020000UL;
    MPU_RegionTypeDef.Size = MPU_REGION_SIZE_128KB;
    MPU_RegionTypeDef.SubRegionDisable = 0x0;
    MPU_RegionTypeDef.TypeExtField = MPU_TEX_LEVEL0;
    MPU_RegionTypeDef.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_RegionTypeDef.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
    MPU_RegionTypeDef.IsShareable = MPU_ACCESS_SHAREABLE;
    MPU_RegionTypeDef.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
    MPU_RegionTypeDef.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

    HAL_MPU_ConfigRegion(&MPU_RegionTypeDef);

    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);

 


Place the RX buffer and TX buffer in D2 RAM region

 

#if defined (__ICCARM__)
  #define DMA_BUFFER \
      _Pragma("location=\".D2_dma_buffer\"")
#else
  #define DMA_BUFFER \
      __attribute__((section(".D2_dma_buffer")))
#endif

#define BUFFER_SIZE           30

DMA_BUFFER uint8_t TxBuffer;
DMA_BUFFER uint8_t RxBuffer[BUFFER_SIZE];

 


Configure EXTI for Data ready event

 

    EXTI_InitTypeDef.Line_0_31     = LL_EXTI_LINE_0;
    EXTI_InitTypeDef.Line_32_63    = LL_EXTI_LINE_NONE;
    EXTI_InitTypeDef.Line_64_95    = LL_EXTI_LINE_NONE;
    EXTI_InitTypeDef.LineCommand   = ENABLE;
    EXTI_InitTypeDef.Mode          = LL_EXTI_MODE_IT;
    EXTI_InitTypeDef.Trigger       = LL_EXTI_TRIGGER_FALLING;

    LL_EXTI_Init(&EXTI_InitTypeDef);

 


Configure SPI

 

    SPI_InitTypeDef.TransferDirection  = LL_SPI_FULL_DUPLEX;
    SPI_InitTypeDef.Mode               = LL_SPI_MODE_MASTER;
    SPI_InitTypeDef.DataWidth          = LL_SPI_DATAWIDTH_8BIT;
    SPI_InitTypeDef.ClockPolarity      = LL_SPI_POLARITY_LOW;
    SPI_InitTypeDef.ClockPhase         = LL_SPI_PHASE_2EDGE;
    SPI_InitTypeDef.NSS                = LL_SPI_NSS_SOFT;
    SPI_InitTypeDef.BaudRate           = LL_SPI_BAUDRATEPRESCALER_DIV8;
    SPI_InitTypeDef.BitOrder           = LL_SPI_MSB_FIRST;
    SPI_InitTypeDef.CRCCalculation     = LL_SPI_CRCCALCULATION_DISABLE;
    SPI_InitTypeDef.CRCPoly            = 0x7;

    LL_SPI_Init(SPI3, &SPI_InitTypeDef);
    LL_SPI_SetStandard(SPI3, LL_SPI_PROTOCOL_MOTOROLA);
    LL_SPI_DisableNSSPulseMgt(SPI3);

    LL_SPI_ClearFlag_EOT(SPI3);
    LL_SPI_ClearFlag_TXTF(SPI3);

 


Configure DMA

 

    DMA_InitTypeDef.PeriphOrM2MSrcAddress = (u32)(LL_SPI_DMA_GetRxRegAddr(SPI3));
    DMA_InitTypeDef.MemoryOrM2MDstAddress = (u32)(&RxBuffer);
    DMA_InitTypeDef.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
    DMA_InitTypeDef.Mode = LL_DMA_MODE_CIRCULAR;
    DMA_InitTypeDef.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
    DMA_InitTypeDef.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
    DMA_InitTypeDef.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
    DMA_InitTypeDef.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
    DMA_InitTypeDef.NbData = BUFFER_SIZE;
    DMA_InitTypeDef.PeriphRequest = LL_DMAMUX1_REQ_SPI3_RX;
    DMA_InitTypeDef.Priority = LL_DMA_PRIORITY_HIGH;
    DMA_InitTypeDef.FIFOMode = LL_DMA_FIFOMODE_DISABLE;
    DMA_InitTypeDef.FIFOThreshold = LL_DMA_FIFOTHRESHOLD_1_4;
    DMA_InitTypeDef.MemBurst = LL_DMA_MBURST_SINGLE;
    DMA_InitTypeDef.PeriphBurst = LL_DMA_PBURST_SINGLE;
    LL_DMA_Init(DMA1, LL_DMA_STREAM_3, &DMA_InitTypeDef);

    LL_DMA_DisableDoubleBufferMode(DMA1, LL_DMA_STREAM_3);
    LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_3);
    LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_3);

    DMA_InitTypeDef.PeriphOrM2MSrcAddress = (u32)(LL_SPI_DMA_GetTxRegAddr(SPI3));
    DMA_InitTypeDef.MemoryOrM2MDstAddress = (u32)(&TxBuffer);
    DMA_InitTypeDef.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
    DMA_InitTypeDef.Mode = LL_DMA_MODE_NORMAL;
    DMA_InitTypeDef.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
    DMA_InitTypeDef.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_NOINCREMENT;
    DMA_InitTypeDef.PeriphRequest = LL_DMAMUX1_REQ_SPI3_TX;
    DMA_InitTypeDef.Priority = LL_DMA_PRIORITY_HIGH;
    LL_DMA_Init(DMA1, LL_DMA_STREAM_4, &DMA_InitTypeDef);

    LL_DMA_DisableDoubleBufferMode(DMA1, LL_DMA_STREAM_4);
    LL_DMA_DisableIT_TC(DMA1, LL_DMA_STREAM_4);
    LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_4);

    NVIC_SetPriority(DMA1_Stream3_IRQn, 9);
    NVIC_EnableIRQ(DMA1_Stream3_IRQn);

 


ISR for EXTI0 Data ready event

 

void EXTI0_IRQHandler(void)
{
    if (LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_0) != RESET)
    {
        LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0);
    
        LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_3, (uint32_t)&RxBuffer);    
        LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_3, (uint32_t)(LL_SPI_DMA_GetRxRegAddr(SPI3)));
        
        LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_3, BUFFER_SIZE);
        LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_4, BUFFER_SIZE);
        
        LL_SPI_EnableDMAReq_RX(SPI3);
        
        LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_3);

        LL_SPI_SetTransferSize(SPI3, BUFFER_SIZE);
        
        LL_SPI_Enable(SPI3);

        LL_SPI_StartMasterTransfer(SPI3);

        LL_SPI_EnableDMAReq_TX(SPI3);

        LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_4);
    }

 


ISR for DMA1 Rx Stream3

 

void DMA1_Stream3_IRQHandler(void)
{
    if (LL_DMA_IsActiveFlag_TC3(DMA1) != RESET)
    {
        LL_DMA_ClearFlag_TC3(DMA1);

        LL_SPI_DisableDMAReq_RX(SPI3);

        LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_3);

        LL_SPI_DisableDMAReq_TX(SPI3);
        
        LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_4);

        ClearDMAFlag(DMA1, LL_DMA_STREAM_3);

        ClearDMAFlag(DMA1, LL_DMA_STREAM_4);

        LL_SPI_ClearFlag_EOT(SPI3);

        LL_SPI_ClearFlag_TXTF(SPI3);

        LL_SPI_Disable(SPI3);
    }
}

 

 

5 REPLIES 5
FBL
ST Employee

Hello @oseaw.1 

 

When an EXTI LINE 0 event (data ready) occurs, attached to the code is a program in which 30 bytes of 0xFF bytes are transmitted to the DMA Tx buffer, and then 30 bytes are read through the DMA Rx buffer.

The problem is that data is not received properly in the DMA receive buffer. Data continues to circulate at the 7th and 21st indices of the receiving buffer. Except for the two indexes, the remaining buffer space is not filled with anything.


The reason behind this, the software must perform a cache invalidate before reading the updated data from SRAM2.

 


I placed the DMA Rx and Tx buffer to D2 RAM and disabled the data cache of the region. And, Even if I add some delay time before starting the DMA read in Line 7 of EXTI0_IRQHandler(), the data transfer problem still occurs.

However, it seems that the DMA transfer is working normally after stopping and resuming by the breakpoint on Line 7 of EXTI0_IRQHandler().


When disabling the data cache, you must clean the entire cache to ensure that any dirty data is flushed. 
 

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

oseaw.1
Associate II
Hello F.Belaid

Thank you for your reply.

As you commented, I disabled the D-Cache (turns off the entire data cache) using SCB_DisableDCache() and cleaned the entinre data cache using SCB_DisableDCache().but, the DMA transfer still not working properly.

 

SCB_DisableDCache();
SCB_CleanDCache();

 

Hello again,

 

You need to clean and invalidate the cache lines (it should be aligned to the buffer size), you can simply use

 

  SCB_InvalidateDCache_by_Addr ((uint32_t *)aRxBuffer, BUFFERSIZE);

 

Make sure that BufferSize parameter is the size of the DMA Rx buffer in bytes.

 

 

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Pavel A.
Evangelist III

SPI transmits and receives simultaneously. Before you start SPI TX (line 21), the TX and RX DMA must be already running! 

 

Hello, 

I was trying to invalidate and clean rxbuffer, and set bits tx and rx requests simultaneously. 
But, It was not working. It seems that It's difficult to control dma in STM32H743.

SCB_InvalidateDCache_by_Addr((uint32_t *)RxBuffer, BUFFER_SIZE);
SET_BIT(SPI3_INST->CFG1, SPI_CFG1_TXDMAEN | SPI_CFG1_RXDMAEN);