cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H5 DMA (GPDMA), why is USART using a "node"? Intent?

MBC
Associate III

 

I'm trying to set up a DMA transfer from UART1 periheral to memory. Using LL drivers. Using STM32Cube for "convenient" setup. It puts the all the USART peripheral init in usart.c. Where for GPDMA it does something like:

 

 

 

 /* GPDMA1_REQUEST_USART1_RX Init */
  NodeConfig.DestAllocatedPort = LL_DMA_DEST_ALLOCATED_PORT0;
  NodeConfig.DestHWordExchange = LL_DMA_DEST_HALFWORD_PRESERVE;
  NodeConfig.DestByteExchange = LL_DMA_DEST_BYTE_PRESERVE;
  NodeConfig.DestBurstLength = 1;
  NodeConfig.DestIncMode = LL_DMA_DEST_INCREMENT;
  NodeConfig.DestDataWidth = LL_DMA_DEST_DATAWIDTH_BYTE;
  NodeConfig.SrcAllocatedPort = LL_DMA_SRC_ALLOCATED_PORT0;
  NodeConfig.SrcByteExchange = LL_DMA_SRC_BYTE_PRESERVE;
  NodeConfig.DataAlignment = LL_DMA_DATA_ALIGN_ZEROPADD;
  NodeConfig.SrcBurstLength = 1;
  NodeConfig.SrcIncMode = LL_DMA_SRC_FIXED;
  NodeConfig.SrcDataWidth = LL_DMA_SRC_DATAWIDTH_BYTE;
  NodeConfig.TransferEventMode = LL_DMA_TCEM_BLK_TRANSFER;
  NodeConfig.Mode = LL_DMA_NORMAL;
  NodeConfig.TriggerPolarity = LL_DMA_TRIG_POLARITY_MASKED;
  NodeConfig.BlkHWRequest = LL_DMA_HWREQUEST_SINGLEBURST;
  NodeConfig.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
  NodeConfig.Request = LL_GPDMA1_REQUEST_USART1_RX;
  NodeConfig.Mode = LL_DMA_NORMAL;
  NodeConfig.UpdateRegisters = (LL_DMA_UPDATE_CTR1 | LL_DMA_UPDATE_CTR2 | LL_DMA_UPDATE_CBR1 | LL_DMA_UPDATE_CSAR | LL_DMA_UPDATE_CDAR | LL_DMA_UPDATE_CTR3 | LL_DMA_UPDATE_CBR2 | LL_DMA_UPDATE_CLLR);
  NodeConfig.NodeType = LL_DMA_GPDMA_LINEAR_NODE;
  LL_DMA_CreateLinkNode(&NodeConfig, &Node_GPDMA1_Channel0);

  LL_DMA_ConnectLinkNode(&Node_GPDMA1_Channel0, LL_DMA_CLLR_OFFSET5, &Node_GPDMA1_Channel0, LL_DMA_CLLR_OFFSET5);

 

 

 

1. Ok... WHY!? I don't understand why Cube decided to make a node for this. It's not part of the linked list feature in Cube. It seems to me, as a guess, that this peripheral uses a single instance of the same linked list format when receiving to an arbitrary buffer instead of its dedicated FIFO. Is that right?

2. I could accept that, except there seems to be no opportunity or good time to add my source address, destination address, and block size (buffer size). These are members at:

 

 

    DMA_InitStruct.DestAddress = LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_TRANSMIT);
    /* Default settings - updated prior start of transmission */
    DMA_InitStruct.SrcAddress = 0x00000000U;
    DMA_InitStruct.BlkDataLength = 0x00000000U;

 

 

Do not exist in the Cube definition. What is the intent on when/where/how to set these? This is in the non-user section of the generated file. I can set them outside of the linked list with functions like `LL_DMA_SetBlkDataLength()`, but that seems incorrect as it isn't using the node/list item. Am I intended to set these were I need them with something like:

 

 

  LL_DMA_InitNodeTypeDef NodeConfig = {0};


      DMA_InitStruct.DestAddress = LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_TRANSMIT);
    DMA_InitStruct.SrcAddress = 0x00000000U;
    DMA_InitStruct.BlkDataLength = 0x00000000U;

// Only update those three?
  NodeConfig.UpdateRegisters = (LL_DMA_UPDATE_CBR1 | LL_DMA_UPDATE_CSAR | LL_DMA_UPDATE_CDAR);

  LL_DMA_CreateLinkNode(&NodeConfig, &Node_GPDMA1_Channel0);

 

 

 

Where I've set the Node once in generated, and partially again later in my code?

3. Once I set the linked list as in #2.. It seems that I do/can use `LL_DMA_SetSrcAddress()` for my memory/buffer. So I guess I could just use those more simple set functions that do not seem to be in the list. It isn't clear when the source/dest/size is going to be pulled from the one-item list, and when it'll be used from the register I set direction. This seems very complicated, I'm sure there is a reason. But any explanation would be helpful.

 

Thanks

2 REPLIES 2

 

The GP1DMA UART for U5/H5 works using linked lists for chaining transfers. Many (most) of the time you would want one transfer repeated over and over. Like UART downloading to a single circular buffer over and over.

The default instance for MX setup without defining a specific linked list seems to prepare for this case. It creates the node, links it to itself, then sets the DMA peripheral to that list of one item.

 

However... it appears that the `Node_GPDMA1_Channel0` variable to define the linked list and node that will point to itself, is defined as local memory. Meaning once you init and leave this function, you can not know what the DMA will use once triggered.

Either this is intended to work "out of the box" after init in which case `Node_GPDMA1_Channel0` should be static/global, or creating the node and linking itself should be removed as there is no point in doing so then immediately destroying that memory. I think.

 

 

 

  LL_DMA_InitNodeTypeDef NodeConfig = {0};
  LL_DMA_LinkNodeTypeDef Node_GPDMA1_Channel0 = {0};
  LL_DMA_InitLinkedListTypeDef DMA_InitLinkedListStruct = {0};

  // Other init code excluded

  /* GPDMA1_REQUEST_USART1_RX Init */
  NodeConfig.DestAllocatedPort = LL_DMA_DEST_ALLOCATED_PORT0;
  NodeConfig.DestHWordExchange = LL_DMA_DEST_HALFWORD_PRESERVE;
  NodeConfig.DestByteExchange = LL_DMA_DEST_BYTE_PRESERVE;
  NodeConfig.DestBurstLength = 1;
  NodeConfig.DestIncMode = LL_DMA_DEST_FIXED;
  NodeConfig.DestDataWidth = LL_DMA_DEST_DATAWIDTH_BYTE;
  NodeConfig.SrcAllocatedPort = LL_DMA_SRC_ALLOCATED_PORT0;
  NodeConfig.SrcByteExchange = LL_DMA_SRC_BYTE_PRESERVE;
  NodeConfig.DataAlignment = LL_DMA_DATA_ALIGN_ZEROPADD;
  NodeConfig.SrcBurstLength = 1;
  NodeConfig.SrcIncMode = LL_DMA_SRC_FIXED;
  NodeConfig.SrcDataWidth = LL_DMA_SRC_DATAWIDTH_BYTE;
  NodeConfig.TransferEventMode = LL_DMA_TCEM_BLK_TRANSFER;
  NodeConfig.Mode = LL_DMA_NORMAL;
  NodeConfig.TriggerPolarity = LL_DMA_TRIG_POLARITY_MASKED;
  NodeConfig.BlkHWRequest = LL_DMA_HWREQUEST_SINGLEBURST;
  NodeConfig.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
  NodeConfig.Request = LL_GPDMA1_REQUEST_USART1_RX;
  NodeConfig.Mode = LL_DMA_NORMAL;
  NodeConfig.UpdateRegisters = (LL_DMA_UPDATE_CTR1 | LL_DMA_UPDATE_CTR2 | LL_DMA_UPDATE_CBR1 | LL_DMA_UPDATE_CSAR | LL_DMA_UPDATE_CDAR | LL_DMA_UPDATE_CTR3 | LL_DMA_UPDATE_CBR2 | LL_DMA_UPDATE_CLLR);
  NodeConfig.NodeType = LL_DMA_GPDMA_LINEAR_NODE;
  LL_DMA_CreateLinkNode(&NodeConfig, &Node_GPDMA1_Channel0);

  LL_DMA_ConnectLinkNode(&Node_GPDMA1_Channel0, LL_DMA_CLLR_OFFSET5, &Node_GPDMA1_Channel0, LL_DMA_CLLR_OFFSET5);

  LL_DMA_SetLinkedListBaseAddr(GPDMA1, LL_DMA_CHANNEL_0, (uint32_t)&Node_GPDMA1_Channel0);
  LL_DMA_ConfigLinkUpdate(GPDMA1, LL_DMA_CHANNEL_0,
                            (LL_DMA_UPDATE_CTR1 | LL_DMA_UPDATE_CTR2 | LL_DMA_UPDATE_CBR1 | LL_DMA_UPDATE_CSAR
                             | LL_DMA_UPDATE_CDAR | LL_DMA_UPDATE_CTR3 | LL_DMA_UPDATE_CBR2 | LL_DMA_UPDATE_CLLR),
                            (uint32_t)&Node_GPDMA1_Channel0);

 

 

 

Saket_Om
ST Employee

Hello @MBC 

 

In the STM32Cube_FW_H5 package, there is an example demonstrating UART communication using DMA linked list. You can refer to this example to update your settings. The configuration for source, destination, and size can be found in the linked_list.c file.

If your question is answered, please close this topic by clicking "Accept as Solution".

Thanks
Omar