cancel
Showing results for 
Search instead for 
Did you mean: 

How to configure the GPDMA

B.Montanari
ST Employee

1. GPDMA module setup

The new DMA module (which includes the GPDMA and the LPDMA) available in series such as the STM32U5 are slightly different than the regular DMA. In this article, we’ll discuss the setup for using GPDMA in a similar way as the standard DMA available on most STM32 series.


2. Application benefits

The main application benefit for using DMA in any application is to off-load the CPU for data transfer from a memory mapped source to a memory mapped destination. In the image below, it is possible to see the power domains associated with the GPDMA and LPDMA:
415.png
As it can be seen in the above image, the GPDMA comes with 2 Ports. The Port0 should be typically allocated for transfers to/from peripherals as there is a direct hardware data path to APB peripherals, outside the AHB matrix. Port1 on the other hand should be typically allocated for transfers to/from memory. In any case, the GPDMA target can be addressed from any port.
Another interesting factor to take into account is that each channel has a FIFO size and it follows this rule: from channel0 to channel 11 the FIFO is 8bytes (2words) – these channels are ideal for transfers from//to an APB/AHB peripheral to SRAM, for example, UART to a buffer. Channels 12 to 15 have a 32byte (8 words) FIFO, so they are better suited for transfers between a high speed AHB peripheral and SRAM, or for transfer from/to external memories.
Alright, now that we got the basics, let’s dive into a real project and see how that goes. For this portion we’ll use STM32CubeIDE (v1.10.1) with the NUCLEO-U575ZI-Q but this code can be easily ported to any STM32 series that has the GPDMA.


3. Create a new Project

Create a new project using the given board as the starting point, this will assign the pins automatically for this board, including the serial port, LED, keys and the USB. If you have any doubts on how to create a project from scratch using a board, we have several articles with this information, but here is one you can use for that purpose (up to the end of step #5). Just remember to select the board with GPDMA you are using to replicate this content. As we are only interested in the UART for this demo, we’ll just disregard the additional portion.
In the *.ioc created, we can locate the “Connectivity” section and expand it to select the USART1, which is connected to the VCOM. Here we’ll use a fairly typical setup for the USART: 115200/8/N/1
416.png


4. Set up the GPDMA

Now, the part we really want to interact with, the GPDMA. If you click in the “DMA Settings” tab, it will show the message to go to GPDMA1:
417.png
Following the previous guidelines for selecting the GPDMA channels, we’ll assign Channels 10 and 11, both with 2 word FIFO and we’ll set them in Standard Request Mode.
418.png
As you’ve noticed in the drop list options there were 2 possible selections, the Standard Request Mode and the Linked List Mode, if you want to know more about the Linked List Mode and why would you use it, please refer to this explanation from ARM’s documentation:
 “A linked list is a list of elements that include pointers to the next element in the list. This means that elements can be placed anywhere in memory and created and removed individually without affecting elements around them. They can be used to create arrays of unknown size in memory."

  • Linked lists are used in the DMA controller to support scatter/gathering through the DMACCxLLI register. This means that the source and destination regions do not need to occupy contiguous areas in memory. Where scatter/gather is not required, the DMACCxLLI register must be set to 0.
  • The source and destination data areas are defined by a series of linked lists and each LLI controls the transfer of one block of data, and then optionally loads a further LLI to continue the DMA operation, or stops the DMA stream. An example is given in Appendix C of the Technical Reference Manual.
  • Note that the DMACCxConfiguration DMA channel configuration register is not part of the linked list item and that the configuration registers are not updated when a new LLI is requested.
  • Note also that programming the DMACCxLLI register when the DMA channel is enabled has unpredictable side effects.


The LLI in the DMA controller consists of four words containing the Source address, Destination Address, LLI register and Control register in that order. For some systems, the LLI data structures can and should be made four-word aligned, to make the loading of LLIs more efficient. Information on how to program the PL080 for scatter/gather is given in Section 3.6 on page 3-35 of the Technical Reference Manual.”
 

5. USART TX and USART RX configuration

OK, now that the difference is clear, let’s configure the 2 channels: one will be used for USART TX and the other one for USART RX. Just for the sake of showing that both Ports work with the same peripheral, the TX will use Port0 and RX will use Port1.
Here is the configuration for the TX:
419.png
Here is the configuration for the RX:
420.png
These were all the needed steps, so we can now generate the code (Alt+K).
In the main.c file, create 2 global buffers:

/* USER CODE BEGIN PV */
uint8_t pRxBuff[10];
uint8_t pTxBuff[10] = "Count:  \r\n";
/* USER CODE END PV */

In the main function, before the endless loop, add the reception call and a local variable that we will use to count from 0..9 in the main loop and then start over again:
 

  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_DMA(&huart1, pRxBuff, 10);
  uint8_t u8Inc = 0x30; // 0x30 => ‘0’ ASCII
  /* USER CODE END 2 */

Inside the endless loop, let’s transmit the same message every 5 seconds:
 

/* USER CODE BEGIN 3 */
		pTxBuff[7] = u8Inc++;
		if(u8Inc > 0x39){
			u8Inc = 0x30;
		}
		HAL_UART_Transmit_DMA(&huart1, pTxBuff, 10);	  HAL_Delay(5000);

Finally, create the reception complete callback to echo whenever the 10 bytes are received.

/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	HAL_UART_Transmit_DMA(&huart1,(uint8_t *) "Message Received!\r\n", sizeof("Message Received!\r\n"));
	HAL_UART_Receive_DMA(&huart1, pRxBuff, 10);
}
/* USER CODE END 4 */


6. Code flow demo

Here is a quick demonstration of the code flow, where we can see the message typed ‘Hello ST!!” in the pRxBuff and the local variable being used to change the message in the pTxBuff:
421.png

Just as a minor note, in case you are not aware, the STM32CubeIDE can be used with its terminal function to send and receive data, which was used in the demonstration above. You can check how to do it in this article and, in case you want to customize your IDE to dark theme, please check this other one.
Hope you enjoyed it!
 

Comments
AHu.2
Associate

Hi!!

I noticed your example uses `HAL_UART_Receive_DMA()`

Would you happen to have an example that uses `HAL_UARTEx_ReceiveToIdle_DMA()` ? I tried to use it but got some very strange behavior. Is there some sort of setting in the IOC configuration in order to properly detect IDLE?

Any help would be greatly appreciated.

Warm regards, Anthony

S_Ong
Associate II

@B.Montanari :

                      HAL_UART_TxCpltCallback Triggered but    HAL_UART_RxCpltCallback was not triggered. Do any more parameters need to be set?

Dev Board uses Nucleo-U5A5ZJ-Q. 

Regards

 

Simon

 

PS: I had been trying GPDMA with the above board with SPI1 using hal_spi_transmitreceive_dma for past 1 months😂. Nothing seems to work. 

 

fed
Associate

@B.Montanari could you please share the full project example? unfortunately the configuration is not working and I'am curious to spot the differences in order to understand why  HAL_UART_RxCpltCallback is not triggered at all.

Version history
Last update:
‎2024-06-04 06:20 AM
Updated by:
Contributors