cancel
Showing results for 
Search instead for 
Did you mean: 

Problems with SPI DMA transfer on STM32H743

Curtis B.
Senior

Hello community,

I am facing massive problems using the SPI1 with DMA transfer on the STM32H743 Nucleo board. Using interrupt based transfer works, but when I try to use DMA I always receive zeros in the receive buffer and I get an overrun error from the SPI peripheral.

As the SPI is configured in full duplex master mode I use the HAL_SPI_TransmitReceive_DMA function to generate a clock signal on the bus when I want to receive data. I configured both TX and RX DMA channels for the SPI. HAL_SPI_TransmitReceive_DMA returns HAL_OK. DMA transfer is completed and SPI_DMATransmitReceiveCplt is called. However, HAL_SPI_TxRxCpltCallback is not called due to the overrun error of the SPI.

I know that there are several rules to obey when you want to use DMA with the STM32H743. The DMA channels can only access RAM in the D1 and D2 region. As I use TrueStudio with the gnuarm compiler I added following section to the linker file:

  .DMA_**** :
{
. = ABSOLUTE(0x30047000);
*(.DMA_*****);
} >RAM_D2

I initialized the buffers in the source code in the following way:

__attribute__((section(".DMA_****"))) RFM22_RX_BUFFER_t RFM22_RX_buffer;
__attribute__((section(".DMA_****"))) uint8_t dump[RX_READ_BYTES];
__attribute__((section(".DMA_****"))) uint8_t dump2[RX_READ_BYTES];

 I verified with the debugger, that the buffer variables are in the correct RAM section.

I also know that using the D-cache can lead to several problems. I configured the memory protection unit in a way that the Buffer section is not cacheable not shareable and not bufferable. I even tried it with cache complete deactivated. But I still get the same erroneous behaviour.

Does anyone have a hint whether I might have missed something important?

Best regards,

Daniel

12 REPLIES 12
Curtis B.
Senior

I got it running know. Somehow, I need to call interrupt based data transfer once. Afterwards DMA transfer operates as supposed to. Hope this will helps others facing the same problem...

TKarl.1
Associate

Hello, I faced the same issue with my project on H743. Received only zeroes with HAL_SPI_TransmitReceive_DMA.

Curtis' workaround helped me. Call HAL_SPI_TransmitReceive once, then you can use HAL_SPI_TransmitReceive_DMA later on.

Receiving works properly this way.

Could be an initialisation problem somewhere in HAL. Could not find out the exact cause until now.

TKarl.1
Associate

I found out that I enabled the SPI peripheral (SPI6 in my case) at the end of the initialisation routine MX_SPI6_Init(). This caused the receive problem described above.

So do not enable the SPI peripheral before using HAL_SPI_TransmitReceive_DMA and receiving data works correctly this way.

Spider
Associate II

Hello!

Trying to launch SPI via DMA data transfer to LCD display. I has created "default" project via cube master.

Created SPI2 for LCD display in 8bit Master Transfer Only mode.

Added DMA1 Stream 0 to SPI2_TX Mem to Perif.

In code I do:

static uint8_t buff[28800] __attribute__ ((aligned (32))); //in result buff in RAM_D1 - i have checked it.
 
MX_SPI2_Init();
MX_DMA_Init();
....
HAL_SPI_Transmit(&hspi2, buff, chunk_size, HAL_MAX_DELAY);  // This Code work FINE.
....
HAL_SPI_Transmit_DMA(&hspi2, buff, chunk_size); //This Code NOT work.

No I or D caches are enabled in Cube.

How to make it work? :tired_face:

wynandsp
Associate III

Good day to all.

I can unfortunately not contribute to the answer, but rather add my name to the list of those struggling to find an answer.

As my fellow STM'rs tried so did I independently and found that the DMA_RX function is not performing at all. Also get zeros whereas the IT_RX does what it should.

  • I have disabled the I and D Caches.
  • Made sure that the buffers are in the correct memory locations above 0x24000000 and also tried 0x30000000.
  • Used MPU to protect access to the Caches in these areas when caches were enabled.
  • Completely slowed down the system clock and also SPI clock.
  • I also made the intervals between SPI accesses around 100ms. So more than ample time.
  • I have tried IT version once as was suggested followed by DMA.
  • I have checked the DMAMUX and DMA setup which looks correct.
    /* SPI4 DMA Init */
    /* SPI4_RX Init */
    hdma_spi4_rx.Instance = DMA2_Stream0;
    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_MEDIUM;
    hdma_spi4_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi4_rx) != HAL_OK)
    {
      Error_Handler();
    }
 
    __HAL_LINKDMA(hspi,hdmarx,hdma_spi4_rx);
 
    /* SPI4_TX Init */
    hdma_spi4_tx.Instance = DMA2_Stream1;
    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_LOW;
    hdma_spi4_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi4_tx) != HAL_OK)
    {
      Error_Handler();
    }
 
    __HAL_LINKDMA(hspi,hdmatx,hdma_spi4_tx);
  • Currently also using STM32Cube_FW_H7_V1.8.0.

I am sending 8 bytes at a time and expecting 8 non zero bytes. As I said the following works

statusDriver = HAL_SPI_TransmitReceive_IT( &hspi4, spiTxBuffer, spiRxBuffer, pObj->packetSize );
 
But 
 
statusDriver = HAL_SPI_TransmitReceive_DMA( &hspi4, spiTxBuffer, spiRxBuffer, pObj->packetSize );                         Gives zeros in spiRxBuffer.

If there is anyone out there that really solved this problem please share your wisdom with us.

Kindest regards

Wynand

Curtis B.
Senior

Hi,

I didn't try it with cache disabled, so it is strange that it does not work in this case. The problem that I described obove maybe actually two problems. One Problem is the overrun error. I encountered this error recently on a H755 with using Rx only. In this case the single interrupt based call prior to DMA didn't help. But then I just used Transmit and Receive and transmitted dummy bytes, that made it work. I didn't go deeper into the problem.

The other issue with the cache needs one more thing that needs to be taken into account. When you use the MPU, you need to be sure that your DMA-buffer is aligned according to the size of the protected area. E.g. if you set the the MPU to 1k, your buffer needs to be aligned to (1024). You need to set this in you linker script. Thats a little but important information that you hardly find in the documentation; it is only bequeathed from father to son. What are your detailed settings for the MPU? I did it like this:

 HAL_MPU_Disable();
 
	  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
	  MPU_InitStruct.Number = Region_Number;
	  MPU_InitStruct.BaseAddress = BaseAdrr;
	  MPU_InitStruct.Size = RegionSize;
	  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_NOT_SHAREABLE;
	  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
	  MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
 
	  HAL_MPU_ConfigRegion(&MPU_InitStruct);
 
 
	  /* Enables the MPU */
	  HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);

wynandsp
Associate III

Hi Curtis,

Thank you for your quick response.

I did not know this about the MPU and alignment. I was my understanding that I could define some area and protect it as I like and then have my buffers in there that I do not want to do cache maintenance on. I will definitely consider what you said. My MPU confg is exactly what you have above only difference is that I made 2k and my buffer both Tx and Rx placed in there 8 bytes big.

I need this thing to work as I have an 8 input analogue sampler card that runs all the control loops for the 3 phase systems. The SPI is connected to an EEPROM which stores my control and process constants. These can be changed during operation and I dont want it to influence the control loops. DMA is what I need and the Tx is perfect, it works as expected. I am fairly into the project already but I have disable all the threads apart from the SPI thread.

As I said I disabled the caches altogether, but the result remains the same. I can do TransmitReceive_IT and that works, but the TransmitReceive_DMA only writes the SPI, but zeros on the receiving buffer. I have also filled the RxBuffer with numbers 25-32 to see that it actually accesses the buffer. When I read 2 bytes number 25 and 26 is replaced with 0 and the rest remain intact.

Just for completeness I will include my write function.

void EEPROM_DRIVER_WriteData( pEepromDriverObject_t pObj )
{
	HAL_StatusTypeDef statusDriver = HAL_ERROR;
	U32 ulNotificationValue;
	BSP_eepromSelect( true );
 
	hTaskToNotify 		= xTaskGetCurrentTaskHandle();
 
	while ( HAL_SPI_GetState( &hspi4 ) != HAL_SPI_STATE_READY );
 
	memcpy( spiTxBuffer, pObj->pBuf, pObj->packetSize );
 
	for ( U8 i=25; i<33; i++ )spiRxBuffer[i-25] = i;
 
	statusDriver = HAL_SPI_TransmitReceive_IT( &hspi4, spiTxBuffer, spiRxBuffer, pObj->packetSize );
	if ( HAL_OK != statusDriver )
	{
		pObj->status = osError;
		return;
	}
	else
		LEDon(LED5);
 
	ulNotificationValue = ulTaskNotifyTake( pdTRUE, osWaitForever );
	if ( ulNotificationValue == 1 )
	{
		pObj->status = osOK;
		if ( pObj->pfCallback != NULL )
		{
			 pObj->pfCallback( pObj );
			 pObj->pfCallback = NULL;
 
			 if ( pObj->trfComplete )
			 {
				 BSP_eepromSelect( false );
			 }
		}
	}
	else
	{
		// OOPS
		pObj->status = osErrorTimeout;
	}
}

The callback functions copies the buffers when required.

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
	BaseType_t HigherPriorityTaskWoken = pdFALSE;
 
	LEDoff(LED5);
 
	if ( hTaskToNotify != NULL )
		vTaskNotifyGiveFromISR( hTaskToNotify, &HigherPriorityTaskWoken );
 
	portEND_SWITCHING_ISR( HigherPriorityTaskWoken );
}

As you can see it is very basic and should work as it does with the IT version.

Anyway thank you again for your answer.

Kindest regards

Wynand

Curtis B.
Senior

Indeed, that is very basic... This DMA stuff can be really annoying. Did you check that HAL_SPI_TxRxCpltCallback is called when using DMA? You mentioned, that you verified that the Buffers are in the correct RAM adress area? Did you checked that at runtime in the debugger? At this point I am at my wit's end :(

Curtis B.
Senior

I just reread my initial post... Contrary to my post yesterday, I did try it with cache disabled. You should have a look on the overrun error of the SPI I believe...