STM32学习笔记:分散加载

发布时间 2023-08-29 21:26:41作者: 老吴家的小阿哲

  分散加载是提高单片机上限的一个非常重要的能力。以STM32H7为例,H7的RAM为:512 Kbytes of AXI-SRAM mapped onto AXI bus on D1 domain,SRAM1 mapped on D2 domain: 128 Kbytes,SRAM2 mapped on D2 domain: 128 Kbytes,SRAM3 mapped on D2 domain: 32 Kbytes,SRAM4 mapped on D3 domain: 64 Kbytes,4 Kbytes of backup SRAM。不同域中的内存速度,能访问的外设都是不同的,因此在使用的时候,需要把握每一块内存的使用,要明白其功能,合理分配变量到合适的内存块中(如需要高速频繁读取的数据放在DTCM中,低速但是大量的数据放在SDRAM中)。这里参考安富莱STM32 V7开发板手册第63页,能知道每块RAM的功能以及起始地址。本篇文章的目的是使用DMA,对DMA调用的变量进行分散加载到D3域。从手册中得知D3域的起始地址为0x38000000

  下面以MDK工程为例,记录分散加载的过程。参考安富莱STM32 V7开发板手册第26章:

  首先,点击魔术棒Option,进入到Linker页面,取消使用Target生成内存分配勾选,打开SCT文件。

  进入后显示如图:

  SCT显示的内容(截图中我的RW_IRAM2后面的名称我有修改,默认的是未修改过的,后面的内容会说明如何修改),和Target页面内容息息相关:

  也就是说SCT文件是根据Target选项卡中设置的内容生成的。这里可以看到我的IRAM2起始地址用的是D3域的内存。但是我的大小填错了,D3域的大小是64KB,应该是0x10000。不过没有关系,我们在第一步中取消掉了从Target生成内存分配,也就是说Tartget中的选项不再影响工程。工程实际的内存分配是按照SCT文件来的。

  SCT文件里面第一第二行,LR_IROM1是加载域(Load Region),ER_IROM1是执行域(Execution Region)。正常而言首地址都是0x0800 0000,大小都是0x0020 0000,即 STM32H7 的 Flash 地址和对应大小。这里我使用的是外部QSPI FLASH,故首地址与大小不一致。SCT文件的第三行*.o (RESET, +First),是表示在启动文件中有个段为RESET的代码段,主要存储了中断向量表。这里将它放在Flash的首地址中。

  第四行*(InRoot$$Sections)是把MDK的一些库文件全部放在根域(加载域和执行域),如_main.o。

  第五行,第六行.ANY (+RO),.ANY (+XO)指将RO和XO中的内容也放在根域中。RO是Read only code,XO是Execulte only code。

  接着往下就是定义RAM空间。可以看到RW_IRAM1对应的是DTCM这一块RAM。而我们想要的是D3域的RAM。所以RW_IRAM2是我们想要的RAM。在每个RAM后面有个指令.ANY(+RW +ZI)这里的RW是可读可写数据,一般理解为变量,ZI是0初始化数据。.ANY的意思是当这一块内存满了以后,后面的数据会顺延到下一块内存,也就是说在这个配置里DTCM的内存满了以后,会自动顺延到D3域中。我们知道DMA只能访问D3域中的数据,所以这里D3域就不用 .ANY指令了,而改用*号。使用*号的意思就是只使用这一块内存,如果溢出则编译器报错。这样就防止内存溢出到别的内存块中,导致DMA无法访问。

  这里需要注意一点,SDRAM在使用的时候需要做初始化,D2域的RAM需要开启时钟。这两块RAM我都没有使用,故本文中没有体现。安富莱的教程说明了这一点。

  因此只需要添加以下代码在IRAM1后即可:

RW_IRAM2 0x38000000 0x00010000  {  ; RW data    64KB SRAM4(0x38000000)
   * (.RAM_D3)
  }

  这里的*表示内存不自动溢出到下一块,.RAM_D3表示这一块内存的名字,可以自己自定义。

  完成之后在main里定义自己想要的变量到D3域即可

  这里要注意定义全局变量,否则会报错。

  在此感谢安富莱的教程,以及群友的帮助。