要用malloc函数需要指定堆栈的位置和大小,我的硬件环境中有SDRAM,所以我想让malloc函数在sdram中分配空间,首先要指定堆栈的开始地址。在keil中打开系统的sct文件,在我硬件环境中时Emc_Yaffs.sct,其内容如下:
LR_IROM1 0x00000000 0x00080000 { ; load region size_region
ER_IROM1 0x00000000 0x00080000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x10000000 0x00010000 { ; RW data
.ANY (+RW +ZI)
}
RW_IRAM2 0x20004000 0x00004000 {
.ANY (+RW +ZI)
}
}
ARM_LIB_HEAP 0xA0000000 0x200000 { ;
RW_HEAP 0xA0000000 0x200000 {
.ANY (HEAP)
}
}
在文件的最后部分,ARM_LIB_HEAP 0xA0000000 0x200000 { //指定堆栈区的大小
RW_HEAP 0xA0000000 0x200000 {
.ANY(HEAP)
}
} 指定了堆栈区的大小,其中0xA0000000 是堆栈的开始地址,0x200000 指定了堆栈区的最大范围。
通过修改这部分的的内容可以变更堆栈的开始地址。
之后就是在启动文件中 变更堆栈大小的定义。使其在刚才指定的大小之内。
startup_stm32f4xx.s 中设定了 Heap_Size EQU 0x00000200大小
__user_initial_stackheap
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
ALIGN
ENDIF
END
__user_initial_stackheap 会给 C库使用malloc
__user_initial_stackheap 库函数用法
__user_initial_stackheap返回初始化堆和栈的位置。
RVCT V2.X及其更早的版本中__user_initial_stackheap默认使用的是符号|Image$$ZI$$Limit|的值。当使用分散
加载文件的时候这个符号不会产生。如果你使用分散加载文件,那么你需要重新执行__user_initial_stackheap
函数,不然链接会失败。
在RVCT V3.X中__user_initial_stackheap的功能得到了加强,即使你使用分散加载文件也不需要你重新执行
__user_initial_stackheap函数。函数会根据你的分散加载文件的描述选择正确的功能函数段。
注意:如果你重新执行__user_initial_stackheap函数,这将忽略所有的库函数的执行。这就使得存在的应用程序不
需要修改。
语法:
__valu_in_regs struct __initial_stackheap __user_initial_stackheap(unsinged R0,
unsigned SP,
unsgined R2)
用法:
__user_initial_stackheap 返回这些值:
1. 堆基址(heap base) ---> RO
2. 栈基址(stack base,一般为栈的最高地址) ---> R1
3. 堆顶(heap limit) ---> R2
4. 栈顶(stack limit) ---> R3
如果这个函数被重新执行,那么必须满足以下条件:
1. 栈的使用不能超过88字节。
2. R12(ip)不能被破坏。
3. 堆要以8字节方式对齐(ALIGN = 3)。
默认的一段模式中R2,R3将被忽略,R0,R1间的存储空间将全部用来作为堆空间。
如果使用二段模式那么堆和栈的空间将分别受R2,R3的限制。
在rt_misc.h中__user_initial_stackheap的定义是这样的:
struct __initial_stackheap {
unsigned heap_base, stack_base, heap_limit, stack_limit
}
注意:
由于满递减堆栈的原因,stack_base的值比栈的最高地址要高出1个栈容量。
返回:
R0,R1,R2,R3中的返回值是由你使用的一段存储模式还是二段存储模式决定。
1. 一段模式中R1比R0大。R2和R3被忽略了。
2. 二段模式中R0和R2用来初始化堆,R1和R3用来初始化栈。R2 >= R0, R3 < R1
┏━━━┳━━━┓<----- R1 ----->┏━━━┳━━━┓<--- HIGH ADDR
┃ ↑ ┃ stack_base ┃ │ ┃
┃ │ ┃ ┃ │ ┃
┃ │ ┃ ┃ stacks ┃
┃ │ ┃ ┃ │ ┃
┃ │ ┃ ┃ ↓ ┃
┃ │ ┃ R3 ----->┃ ┻ ┃
┃ ↑ ┃ ┃ ┃
┃ │ ┃ ┃ ┃
┃ │ ┃ ┃ ┃
┃ │ ┃ R2 ----->┃ ┳ ┃
┃ │ ┃ ┃ ↑ ┃
┃ │ ┃ ┃ │ ┃
┃ │ ┃ ┃ │ ┃
┃ heaps ┃ ┃ heaps ┃
┃ │ ┃ ┃ │ ┃
┃ │ ┃ ┃ │ ┃
┃ │ ┃ ┃ │ ┃
┗━━━┻━━━┛<----- R0 ----->┗━━━┻━━━┛<--- LOW ADDR
_use_one_memory_region _use_two_memory_region
使用分散加载文件:
默认情况下__user_initial_stackhep()使用的是符号|Image$$ZI$$limit|的值。这个符号在用户使
用分散加载文件时不会产生。然而,C库提供了比较折中的方法:利用分散加载文件中的一些信息来
执行这个函数。
注意:
如果你重新执行__user_initial_stackhep()函数,那么其他的库的执行将被忽略。
选择使用一段存储模式:
定义一段特殊的执行域在你的分散加载文件中。使用这种符号:ARM_LIB_STACKHEAP,并使用EMPTY
属性。
这样库管理器就选择了一个把这个域当作堆和栈合并在一起的__user_initial_stackhep()函数。在
这个函数中使用了Image$$ARM_LIB_STACKHEAP$$Base和Image$$ARM_LIB_STACKHEAP$$ZI$$Limit符号
。
选择使用二段存储模式:
定义两个特殊的执行域再你的分散加载文件中。使用这两种符号:ARM_LIB_STACK, ARM_LIB_HEAP。
并且两个段都要使用EMPTY属性。
这样库管理器就会选择使用符号:Image$$ARM_LIB_HEAP$$Base、
Image$$ARM_LIB_STACK$$ZI$$ZI$$limit、
Image$$ARM_LIB_STACK$$Base
Image$$ARM_LIB_STACK$$ZI$$Limit
的__user_initial_stackhep()函数。
例子:
FLASH_LOAD 0x0000 0x00200000
{
VECTORS +0 0x400
{
* (:gdef:__vectab_stack_and_reset, +FIRST)
; 其他域可以放在这里
}
;; 最高256 异常深度 (256*4bytes == 1024 == 0x400)
CODE 0x400 FIXED
{
* (+RO)
}
DATA 0x20000000 0x00200000
{
* (+RW, +ZI)
}
;; 堆开始于 1MB ,向高地址生长
ARM_LIB_HEAP 0x20100000 EMPTY 0x100000-0x8000
{
}
;; 堆安排在 2MB RAM的最高地址处
;; 向低地址生长,空间为32KB
ARM_LIB_STACK 0x20200000 EMPTY - 0x8000
{
}
}