stm32管理内存分配以及CCM的使用方法

  • Post author:
  • Post category:其他




前言

本文以stm32f407VGT6为例描述,查阅该芯片的手册,可以知道其内存一共192K,但是在MDK编译项目时,有时候发现内存总量只有128k,原因在于,192k中有64k是CCM内存,剩下的128K才是正常的内存,所谓的CCM内存是cpu直接访问的空间,读写速度比其它内存快,但是不支持DMA,所以DMA对应的内存空间不能放在CCM中。



一、使用CCM

打开mdk项目options,target选项卡,右下角可以配置使用的内存空间,注意新版的cube自动生成的mdk项目,默认是不使用ccm的,所以默认配置下IRAM1和IRAM2都是128k的地址空间,首地址是0x20000000,只不过将128k划分为两段。

0x1c000+0x4000刚好是128k

我们得手动修改才能激活使用CCM,首先将IRAM1大小改为0x20000,即整个128k空间,然后将IRAM2改为CCM的首地址0x10000000,大小为0x10000。

在这里插入图片描述

这样配置后,如果程序没有用到DMA,已经可以正常使用了。系统会自动将整个项目程序的data、bss等内容分配到IRAM1和IRAM2中。

但是如果程序中用到了DMA,就存在风险了,一旦DMA对应的内存被自动分配到了IRAM2 ccm中,运行就会出错。



二、手动管理内存分配

为了解决这个问题,我们需要手动分配内存块,首先,去掉linker里面的 use memory layout from Target Dialog,那么就取消了自动内存分配,我们需要手动加载自定义的sct文件,手动配置的sct文件可以放在任意路径下,linker里面 scatter file可以指定sct文件的路径。这里我自己的sct文件叫my.sct还是放在原来的sct文件目录下,即MDK-ARM\项目名称\my.sct。

在这里插入图片描述

sct文件功能很强大,能够以多种方法进行内存的配置,比如,直接指定某个c文件使用的内存地址空间,另外,在代码中申请内存空间的时候,还可以用__attribute__((at(内存地址)))或__attribute__((section(”段别名“)))这个后缀来直接指定内存地址或内存段别名,二者配合使用可以很方便的分配使用的内存。

打开my.sct,修改内容如下:

; ************************************************************* 
; *** Scatter-Loading Description File generated by uVision *** 
; ************************************************************* 
LR_IROM1 0x08000000 0x00100000  {    ; load region size_region 
  ER_IROM1 0x08000000 0x00100000  {  ; load address = execution address 
   *.o (RESET, +First) 
   *(InRoot$$Sections) 
   .ANY (+RO) 
   .ANY (+XO) 
  } 
  RW_IRAM1 0x20000000 0x00020000  {  ; RW data 
   .ANY (+RW +ZI) 
   .ANY (ORIRAM) 
  } 
  RW_IRAM2 0x10000000 0x00010000  { 
   startup_stm32f407xx.o(Heap) 
   heap_4.o(+RW +ZI) 
   .ANY(CCMRAM) 
  } 
} 

这个配置的目标是分别将 startup_stm32f407xx.s中的Heap和 heap_4.c包含的所有数据放在了CCM IRAM2中,另外设置了一个段别名CCMRAM,凡是定义时指定了这个别名的数据,也会放入IRAM2中。同时在IRAM1中也设置了一个段别名ORIRAM,用来放置DMA使用的内存空间。其它data和bss数据都默认保存在IRAM1中( .ANY (+RW +ZI) )。

在程序main.c中,分别定义两个数组:

__align(32) uint8_t DmaMenBase[DMA_BUF_SIZE] __attribute__((section("ORIRAM"))); 
__align(32) uint8_t RapidMemBase[1024] __attribute__((section("CCMRAM")));

第一个DmaMenBase是串口接收DMA内存,指定放在ORIRAM section中,也就是IRAM1中避免读写错误,第二个RapidMemBase数组则指定放在CCMRAM section中,也就是IRAM2。设置完后编译程序,如果配置正确不会报错。



三、结果验证

编译完成后,打开map文件(和sct在同一个目录,即MDK-ARM\项目名称\XX.map),搜索 RW_IRAM2,可以看到如下内容:

 Execution Region RW_IRAM2 (Exec base: 0x10000000, Load base: 0x0800396c, Size: 0x00004220, Max: 0x00010000, ABSOLUTE, COMPRESSED[0x0000000c]) 
Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object 
0x10000000   COMPRESSED   0x00000020   Data   RW         4603    .data               heap_4.o 
0x10000020   COMPRESSED   0x00000400   Data   RW           17    CCMRAM              main.o 
0x10000420        -       0x00003c00   Zero   RW         4602    .bss                heap_4.o 
0x10004020        -       0x00000200   Zero   RW            2    HEAP                startup_stm32f407xx.o 
Execution Region RW_IRAM1 (Exec base: 0x20000000, Load base: 0x0800395c, Size: 0x000013a0, Max: 0x00020000, ABSOLUTE, COMPRESSED[0x00000010]) 
Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object 
0x20000000   COMPRESSED   0x00000004   Data   RW          227    .data               freertos.o 
0x20000004   COMPRESSED   0x0000000c   Data   RW         1372    .data               stm32f4xx_hal.o 
0x20000010   COMPRESSED   0x00000004   Data   RW         2937    .data               system_stm32f4xx.o 
0x20000014   COMPRESSED   0x0000003c   Data   RW         3601    .data               tasks.o 
0x20000050   COMPRESSED   0x00000014   Data   RW         3932    .data               timers.o 
0x20000064   COMPRESSED   0x00000004   Data   RW         4157    .data               cmsis_os2.o 
0x20000068   COMPRESSED   0x0000000c   Data   RW         4659    .data               port.o 
0x20000074   COMPRESSED   0x0000000c   PAD 
0x20000080   COMPRESSED   0x00000100   Data   RW           18    ORIRAM              main.o 
0x20000180        -       0x000000a4   Zero   RW          293    .bss                usart.o 
0x20000224        -       0x00000048   Zero   RW          435    .bss                stm32f4xx_hal_timebase_tim.o 
0x2000026c        -       0x00000040   Zero   RW         3176    .bss                queue.o 
0x200002ac        -       0x000004c4   Zero   RW         3600    .bss                tasks.o 
0x20000770        -       0x00000118   Zero   RW         3931    .bss                timers.o 
0x20000888        -       0x000006b8   Zero   RW         4156    .bss                cmsis_os2.o 
0x20000f40        -       0x00000060   Zero   RW         4783    .bss                c_w.l(libspace.o) 
0x20000fa0        -       0x00000400   Zero   RW            1    STACK               startup_stm32f407xx.o

可以看到 heap_4.o 的data和bss 以及 startup_stm32f407xx.o 的Heap都放在了IRAM2里,IRAM2还包含main.o的CCMRAM段,大小为0x400,即1024字节,对应mian.c中定义的RapidMemBase。而IRAM1则包含了main.o的ORIRAM段,大小为0x100,即256字节,对应mian.c中定义的DmaMenBase。证明自定义配置已经生效。

注意,在程序代码中定义的数组,需要在代码段中确实使用到,否则可能会被编译器优化掉,那么在map中就看不到了。



版权声明:本文为chubbykkk原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。