如何配置GPDMA?

1.GPDMA模块设置

STM32U5等系列新DMA模块(包括GPDMA和LPDMA)与常规DMA略有不同。在本文中,我们将以与大多数STM32系列上可用的标准DMA类似的方式讨论使用GPDMA的设置。


2.应用效益

在任何应用程序中使用DMA的主要应用程序好处是卸载CPU,以便将数据从存储器映射的源传输到存储器映射的目的地。在下图中,可以看到与GPDMA和LPDMA相关联的电源域:

如上图所示,GPDMA配有2个端口。端口0通常应被分配用于与外围设备之间的传输,因为在AHB矩阵之外有一条到APB外围设备的直接硬件数据路径。另一方面,端口1通常应被分配用于向存储器传输/从存储器传输。在任何情况下,GPDMA目标都可以从任何端口寻址。
另一个需要考虑的有趣因素是,每个通道都有一个FIFO大小,并且遵循以下规则:从通道0到通道11,FIFO为8字节(2个字)——这些通道非常适合从//传输到SRAM的APB/AHB外围设备,例如UART到缓冲区。通道12至15具有32字节(8个字)FIFO,因此它们更适合在高速AHB外围设备和SRAM之间传输,或从外部存储器传输。
好了,既然我们已经掌握了基本知识,让我们深入到一个真正的项目中,看看进展如何。对于这一部分,我们将使用STM32CubIDE(v1.10.1)和NUCLEO-U575ZI-Q,但此代码可以很容易地移植到任何具有GPDMA的STM32系列。


3.创建新项目

使用给定的板作为起点创建一个新项目,这将自动为该板分配引脚,包括串行端口、LED、密钥和USB。如果您对如何使用板从头开始创建项目有任何疑问,我们有几篇文章提供了这些信息,但这里有一篇您可以用于此目的(直到步骤#5结束)。只需记住选择您用于复制此内容的带有GPDMA的板。由于我们只对这个演示的UART感兴趣,我们将忽略附加部分。
在创建的*.ioc中,我们可以找到“连接”部分,并将其展开以选择连接到VCOM的USART1。在这里,我们将使用USART的一个相当典型的设置:115200/8/N/1


4.设置GPDMA

现在,我们真正想与之交互的部分,GPDMA。如果您单击“DMA Settings”(DMA设置)选项卡,它将显示要转到GPDMA1的消息:

按照前面选择GPDMA通道的指南,我们将分配通道10和11,这两个通道都具有2字FIFO,并将它们设置为标准请求模式。

正如您在下拉列表选项中所注意到的,有两种可能的选择,标准请求模式和链表模式。如果您想了解更多关于链表模式的信息以及为什么要使用它,请参阅ARM文档中的解释:
“链表是一个元素列表,其中包括指向列表中下一个元素的指针。这意味着元素可以放置在内存中的任何位置,并且可以单独创建和删除,而不会影响它们周围的元素。它们可以用于在内存中创建未知大小的数组。”
  • DMA控制器中使用链表来支持通过DMACCxLLI寄存器进行分散/收集。这意味着源和目标区域不需要占用内存中的连续区域。如果不需要分散/聚集,则DMACCxLLI寄存器必须设置为0。
  • 源和目的地数据区域由一系列链表定义,每个LLI控制一个数据块的传输,然后可选地加载另一个LLI以继续DMA操作,或者停止DMA流。《技术参考手册》附录C中给出了一个示例。
  • 注意,DMACCxConfiguration DMA信道配置寄存器不是链表项的一部分,并且当请求新的LLI时,配置寄存器不会更新。
  • 还要注意,当DMA通道被启用时,对DMACCxLLI寄存器进行编程会产生不可预测的副作用。

DMA控制器中的LLI由四个字组成,依次包含源地址、目标地址、LLI寄存器和控制寄存器。对于一些系统,LLI数据结构可以并且应该是四字对齐的,以使LLI的加载更加有效。《技术参考手册》第3-35页第3.6节提供了关于如何对PL080进行散射/聚集编程的信息。”
 

5.USART TX和USART RX配置

好吧,既然区别很明显,让我们配置两个通道:一个用于USART TX,另一个用于USART RX。只是为了显示两个端口与同一外围设备一起工作,TX将使用端口0,RX将使用端口1。
以下是TX的配置:

以下是RX的配置:

这些都是所需的步骤,所以我们现在可以生成代码(Alt+K)。
在主.c文件中,创建2个全局缓冲区:
/*用户代码开始PV*/uint8_t pRxBuff[10];uint8_t pTxBuff[10]=“计数:\r\n”;/*用户代码端PV*/
在主函数中,在无休止循环之前,添加接收调用和本地变量,我们将使用该变量在主循环中从0..9开始计数,然后重新开始:
 
/*用户代码开始2*/HAL_UART_Receive_DMA(&huart1,pRxBuff,10);uint8_t u8Inc=0x30;//0x30=>'0'ASCII/*用户代码结束2*/
在无休止的循环中,让我们每5秒发送一次相同的消息:
 
/*用户代码开始3*/pTxBuff[7]=u8Inc++;如果(u8Inc>0x39){u8Inc=0x30;}HAL_UART_Transmit_DMA(&huart1,pTxBuff,10);HAL_延迟(5000);
最后,创建接收完整回调,以便在接收到10个字节时进行回显。
 
/*用户代码开始4*/void HAL_UART_RxCpltCallback(UART_HandleTypeDef*huart){HAL_UART_Transmit_DMA(&huart1,(uint8_t*)“收到的消息!\r\n”,sizeof(“收到的信息!\r\n”));HAL_UART_Receive_DMA(&huart1,pRxBuff,10);}/*用户代码结束4*/


6.代码流演示

这里是代码流的快速演示,我们可以看到键入的消息“Hello ST!!”在pRxBuff和用于改变pTxBuff中的消息的局部变量中:

需要注意的是,如果您不知道,STM32CubeIDE可以与其终端功能一起用于发送和接收数据,这在上面的演示中使用过。你可以在本文中检查如何做到这一点,如果你想将IDE自定义为深色主题,请检查另一个。
希望你喜欢!