Skip to main content
oseaw.1
Associate
November 7, 2023
Question

SPI DMA transfer issue on STM32H743

  • November 7, 2023
  • 3 replies
  • 4967 views

 

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);
 }
}

 

 

This topic has been closed for replies.

3 replies

Technical Moderator
November 7, 2023

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 "Best answer" on the reply which solved your issue or answered your question.Best regards,FBL
oseaw.1
oseaw.1Author
Associate
November 8, 2023
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();

 

Technical Moderator
November 8, 2023

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 "Best answer" on the reply which solved your issue or answered your question.Best regards,FBL
Pavel A.
Super User
November 8, 2023

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

 

oseaw.1
oseaw.1Author
Associate
November 15, 2023

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);