解决STM32H7系列MCU DMA无法传输数据的问题

在把 DMA 功能从 STM32F7 移植到 STM32H7 MCU 上时,发现 DMA 无法传输数据,具体表现是无法进入回调函数,通过搜索发现 ST 论坛上也有人遇到此问题(STM32H7,STM32H743 ADC with DMA),参考帖子的解决方法,并对比 STM32F7 和 STM32H7 的用户手册,发现问题出在 STM32H7 内部 SRAM 地址映射上,造成 DMA 无法访问目标地址。

参考资料:RM0410STM32F76xxx and STM32F77xxx advanced Arm®-based 32-bit MCUsV4.0

一、STM32F7 和 STM32H7 的内部 SRAM 地址映射的区别

STM32F7 内部 SRAM 地址映射如图 1 所示。

解决STM32H7系列MCU DMA无法传输数据的问题
图 1 STM32F7 系列 MCU 的内部地址映射(RM0410.Rev4,page76)

重点看图中红框圈出来的部分,也就是 0x2000 0000 ~ 0x3FFF FFFF 这一段,包含了 DTCM(128K)、SRAM1(368K)、SRAM2(16K)和保留的部分。其中,0x2000 0000 为 DTCM 的起始地址,长度为 0x20000;0x2002 0000 为 SRAM1 的起始地址,长度为 0x5C000;0x2007 C000 为 SRAM2 的起始地址,长度为 0x4000;所以总共可用的内部 SRAM 长度为 0x80000。

所以,MDK 默认程序运行起始地址为 0x2000 0000,长度为 0x20000,如图 2 所示。

解决STM32H7系列MCU DMA无法传输数据的问题
图 2 STM32F7 系列 MCU MDK 默认 RAM 配置

这刚好对应的是 DTCM 的起始地址和长度,这部分的数据总线为 64 位,运行起来较快。

对于 STM32F7 而言,DTCM 除了被 M7 内核直接访问之外,还可以被 GP-DMAs and peripherals DMAs 访问

The DTCM and ITCM RAMs (tightly coupled memories) are not part of the bus matrix.

The Data TCM RAM is accessible by the GP-DMAs and peripherals DMAs through specific

AHB slave bus of the CPU.

The instruction TCM RAM is reserved only for CPU. it is accessed at CPU clock speed with

0 wait states. The architecture is shown in Figure 1.

RM0410.Rev4,page72

This bus connects the AHB Slave bus of the Cortex®-M7 to the BusMatrix. This bus is used

by DMAs and Peripherals DMAs for Data transfer on DTCM RAM only.

The ITCM bus is not accessible on AHBS. So the DMA data transfer to/from ITCM RAM is

not supported. For DMA transfer to/from Flash on ITCM interface, all the transfers are

forced through AHB bus.

RM0410.Rev4,page73

再来看一下 STM32F7 的总线架构(上部分为主设备,右侧的部分为从设备),如图 3 所示。

解决STM32H7系列MCU DMA无法传输数据的问题
图 3 STM32F7 系列 MCU 系统架构(RM0410.Rev4,page71)

浅橙色框部分就是 GP-DMAs and peripherals DMAs,访问路径见红色的箭头线条(DMA2 访问路径示例)。

GP-DMAs 容易理解,就是 DMA1 和 DMA2,而 peripherals DMAs 则是带有专用 DMA 的外设控制器,如 ETH、USB OTG HS 和 LCD-TFT 等等。所以,浅橙色框的部分都可以访问到 DTCM。GP-DMAs and peripherals DMAs 能访问 DTCM 的意思就是其可以在 DTCM 内开辟内存,并具有读写权限

因此在使用 STM32F7 做 ETH 或者 USB OTG HS 开发的时候,使用 MDK 默认配置的 SRAM 起始地址就可以成功。但是将 STM32F7 的 DMA 代码移植到 STM32H7 之后,就不工作了,这是因为 STM32H7 的总线架构和内部 SRAM 更加复杂。参见 STM32H7 的总线架构和内部 SRM 的文章:

STM32H7系列MCU总线架构及内部SRAM介绍

STM32H7系列MCU总线架构及内部SRAM介绍

STM32H7 系列 MCU 相较于 F7 系列 MCU,拥有复杂的总线矩阵,这些总线矩阵直接关系到 STM32H7 时钟树的配置和内部 SRAM 的操作。这里就简单介绍一下 ST…

根据文章里的描述,DTCM 起始地址仍是 0x2000 0000,长度为 0x20000,但是 DTCM 只能被 M7 内核访问,其他主设备,如 GP-DMAs and peripherals DMAs 均不具备访问权限,所以如果仍旧使用 MDK 默认配置的 SRAM 起始地址,DMA 肯定无法传输数据。也就是说通用 SRAM 的地址变了,但是编译器还是把 DTCM 的地址作为通用 RAM 的起始地址,所以造成 DMA 无法传输数据。

二、解决办法

下面给出 3 种解决办法,这 3 种解决办法不仅限于解决 DMA 的数据传输问题,同时也可以作为 STM32H7 系列 MCU 的内存管理方法。

2.1 修改项目配置

选择支持 DMA 访问的 AXI SRAM 区域作为通用 RAM 起始地址,如图 4 所示。

解决STM32H7系列MCU DMA无法传输数据的问题
图 4 STM32H7 系列 MCU MDK 配置 RAM

使用 DMA 传输时把内存起始地址修改为 D1 域。也就是把勾打在 RAM2 上。这是最简单的解决方法。

2.2 修改通用 RAM 地址

使用 __attribute__((section( )))指定 DMA 访问的目标地址。

在定义数组时,通过 __attribute__((section(“.ARM.__at_address”)))指令把数组地址分配到 AXI SRAM 区域内即可(此时不需要修改项目配置)。不过,这种方法指定的地址只能在 bss 段里,bss 段所在的内存区域在 ld 文件里看,看最后指向哪个 RAM 区域。

下面以 SAI 外设为例:

// 指定数组地址
uint8_t SAI_Buffer_A[2 * 4] __attribute__((section(".ARM.__at_0x24000000")));
 
...
 
// 开始 DMA 传输
HAL_SAI_Receive_DMA(&hsai_BlockA1, SAI_Buffer_A, 2)
 
...
 
// SAI 接收完成回调
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
{
  ...
}

2.3 分散加载多块内存

这种方法对于 STM32H7 系列 MCU 的开发尤其适用。一般情况下,开发环境都会生成一个内存加载文件,不同开发环境生成的内存加载文件的扩展名和位置不一样,例如 STM32CubeIDE 生成的内存加载文件扩展名为.ld,Keil 生成的内存加载文件扩展名为.sct。以.ld 扩展名为例简单介绍一下。

在 ld 文件中添加:

  .lwip_sec (NOLOAD) : {
    . = ABSOLUTE(0x30040000);
    *(.RxDecripSection) 
    
    . = ABSOLUTE(0x30040060);
    *(.TxDecripSection)
    
    . = ABSOLUTE(0x30040200);
    *(.RxArraySection) 
  } >RAM3_D2 AT> FLASH

使用该内存:

__attribute__((section(".RxDecripSection"))) ETH_DMADescTypeDef  DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
__attribute__((section(".TxDecripSection"))) ETH_DMADescTypeDef  DMATxDscrTab[ETH_TX_DESC_CNT]; /* Ethernet Tx DMA Descriptors */
__attribute__((section(".RxArraySection"))) uint8_t Rx_Buff[ETH_RX_DESC_CNT][ETH_RX_BUFFER_SIZE]; /* Ethernet Receive Buffer */

通过以上三种方案,就可以解决 STM32H7 系列 MCU 内存管理问题。

扫码关注尚为网微信公众号

尚为网微信公众号
每天学习电路设计嵌入式系统的专业知识,关注一波,没准就用上了。

原创文章,作者:sunev,如若转载,请注明出处:https://www.sunev.cn/embedded/932.html

(1)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2021年3月15日 19:52
下一篇 2021年3月20日 17:30

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注