版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、三、STM32堆栈区预备知识:一个由C/C+编译的程序占用的内存分为以下几个部分:l 栈区(stack):编译器自动分配释放,存放函数的参数值,局部变量的值等。操作方式类似于数据结构中的栈。l 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。l 全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另 一块区域。程序结束后由系统释放。l 文字常量区 常量字符串就是放在这里的。 程序结束后由系统释放l 程
2、序代码区存放函数体的二进制代码编译后,各个区存储内容举例说明如下:/main.cppint a = 0; 全局初始化区char *p1; 全局未初始化区main()int b; 栈char s = “abc”; 栈char *p2; 栈char *p3 = “123456”; 1234560在常量区,p3在栈上static int c =0; 全局(静态)初始化区p1 = (char *)malloc(10);p2 = (char *)malloc(20);分配得来得10和20字节的区域就在堆区。strcpy(p1, "123456"); 1234560放在常量区,编译器可
3、能会将它与p3所指向的"123456"优化成一个地方。STM32的分区STM32的分区从0x2000 0000(0x2000 0000是SRAM的起始地址,由此可知,堆栈等都是在RAM中的)开始。静态区,堆,栈。所有的全局变量,包括静态变量之类的,全部存储在静态存储区。 紧跟静态存储区之后的,是堆区(如没用到malloc,则没有该区),之后是栈区,栈在程序中存储局部变量。先看启动文件startup_stm32f10x_md.s的定义:; Amount of memory (in bytes) allocated for Stack Tailor thi
4、s value to your application needs <h> Stack Configuration <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> </h>Stack_Size EQU 0x00000400AREA STACK, NOINIT, READWRITE, ALIGN=3 Stack_Mem SPACE Stack_Size _initial_sp; <h> Heap Configuration &l
5、t;o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> </h>Heap_Size EQU 0x00000200AREA HEAP, NOINIT, READWRITE, ALIGN=3 _heap_base Heap_Mem SPACE Heap_Size _heap_limit这里定义了堆栈各自大小,堆:512bytes 栈1k;所以栈区大小有限制,我们在局部变量中不要定义大数组否则容易溢出。再看下code ro rw zi l Code指存储到flash【Rom】中的程序代码。l Z
6、I英语是zero initial,就是程序中用到的变量并且被系统初始化为0的变量的字节数,keil编译器默认是把你没有初始化的变量都赋值一个0,这些变量在程序运行时是保存在RAM中的。l RW是可读可写变量,就是初始化时候就已经赋值了的,RW + ZI就是你的程序总共使用的RAM字节数。l RO是程序中的指令和常量,这些值是被保存到Rom中的。l Total ROM Size (Code + RO Data + RW Data)这样所写的程序占用的ROM的字节总数,也就是说程序所下载到ROM flash 中的大小。为什么Rom中还要存RW,因为掉电后RAM中所有数据都丢失了,每次上电RAM中的
7、数据是被重新赋值的,每次这些固定的值就是存储在Rom中的,为什么不包含ZI段呢,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可。包含进去反而浪费存储空间。 实际上,ROM中的指令至少应该有这样的功能: 1. 将RW从ROM中搬到RAM中,因为RW是变量,变量不能存在ROM中。 2. 将ZI
8、所在的RAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI地址及大小来将相应得RAM区域清零。ZI中也是变量,同理:变量不能存在ROM中。 1,首先来看:栈(STACK)的问题.函数的局部变量,都是存放在“栈”里面,栈的英文是:STACK。STACK的大小,我们可以在stm32的启动文件里面设置,在startup_stm32f10x_hd.s里面,开头就有: Stack_Size EQU 0x00000800表示栈大小是0X800,也就是2048
9、字节,这样,CPU处理任务的时候,函数局部变量过多可占用的大小就是:2048字节。注意:是所有在处理的函数,包括函数嵌套,递归,等等,都是从这个“栈”里面,来分配的。所以,如果一个函数的局部变量过多,比如在函数里面定义一个u8 buf512,这一下就占了1/4的栈大小了,再在其他函数里面来搞两下,程序崩溃是很容易的事情,这时候,一般你会进入到hardfault.这是初学者非常容易犯的一个错误.切记不要在函数里面放N多局部变量,尤其有大数组的时候!对于栈区,一般栈顶,也就是MSP,在程序刚运行的时候,指向程序所占用内存的最高地址。比如附件里面的这个程序序,内存占用如下图:图中,我们可以看到,程序
10、总共占用内存:20+2348字节=2368=0X940那么程序刚开始运行的时候:MSP=0X2000 0000+0X940=0X2000 0940.事实上,也是如此,如图:图中,MSP就是:0X2000 0940,程序运行后,MSP就是从这个地址开始,往下给函数的局部变量分配地址。再说说栈的增长方向,我们可以用如下代码测试: /保存栈增长方向/0,向下增长;1,向上增长static u8 stack_dir; /查找栈增长方向,结果保存在stack_dir里面void find_stack_direction(void) static u8 *addr=NU
11、LL; /用于存放第一个dummy的地址。 u8 dummy; /用于获取栈地址 if(addr=NULL) /第一次进入
12、160; addr=&dummy; /保存dummy的地址 find_stack_direction (); /递归 else
13、0; /第二次进入 if(&dummy>addr)stack_dir=1; /第二次dummy的地址大于第一次dummy,那么说明栈增长方向是向上的. else stack_dir=0; &
14、#160; /第二次dummy的地址小于第一次dummy,那么说明栈增长方向是向下的. 这个代码不是我写的,网上抄来的,思路很巧妙,利用递归,判断两次分配给dummy的地址,来比较栈是向下生长,还是向上生长。如果你在STM32测试这个函数,你会发现, STM32的栈,是向下生长的。事实上,一般CPU的栈增长方向,都是向下的。2,再来说说,堆(HEAP)的问题.全局变量,静态变量,以及内存管理所用的内存,都是属于“堆"区”,英文名:“HEAP”,
15、与栈区不同,堆区,则从内存区域的起始地址开始分配给各个全局变量和静态变量。堆的生长方向,都是向上的。在程序里面,所有的内存分为:堆+栈,只是他们各自的起始地址和增长方向不同,他们没有一个固定的界限,所以一旦堆栈冲突,系统就到了崩溃的时候了。同样,我们用附件里面的例程测试:stack_dir的地址是0X2000 0004,也就是STM32的内存起始端的地址。这里本来应该是从0X2000 0000开始分配的,但是,我仿真发现0X2000 0000总是存放:0X2000 0398,这个值,貌似是MSP,但是又不变化,还请高手帮忙解释下。其他的,全局变量,则依次递增,地址肯定大于0X20000004,
16、比如cpu_endian的地址就是0X20000005。这就是STM32内部堆的分配规则.3,再说说,大小端的问题.大端模式:低位字节存在高地址上,高位字节存在低地址上 小端模式:高位字节存在高地址上,低位字节存在低地址上STM32属于小端模式,简单的说,比如u32 temp=0X12345678;假设temp地址在0X2000 0010.那么在内存里面,存放就变成了:地址 |
17、60; HEX |0X2000 0010 | 78 56 43 12 |CPU到底是大端还是小端,可以通过如下代码测试:/CPU大小端/0,小端模式;1,大端模式.static u8 cpu_endian;/获取CPU大小端模式,结果保存在cpu_endian里面void find_cpu_endian(
18、void) int x=1; if(*(char*)&x=1)cpu_endian=0; /小端模式 else cpu_endian=1; /大端模式 以上测试,在STM32上,你会得到cpu_endian=0,也就是小端模式.3,最后说说,STM32内存的问题. 还是以附件工程为例,在前面第一个图,程序总共占用内存:20+2348字节,这么多内存,到底是怎么得来的呢?我们可以双击Project侧边栏的:Targt1,会
19、弹出test.map,在这个里面,我们就可以清楚的知道这些内存到底是怎么来的了.在这个test.map最后,Image 部分有:=Image component sizes Code (inc. data) RO Data RW Data ZI Data Debug Object Name 172
20、60; 10 0 4 0 995 delay.o/delay.c里面,fac_us和fac_ms,共占用4字节
21、; 112 12 0 0 0 427
22、 led.o 72 26 304 0 2048
23、160; 828 startup_stm32f10x_hd.o /启动文件,里面定义了Stack_Size为0X800,所以这里是2048. 712 52 0 0
24、60; 0 2715 sys.o 348 154 0
25、; 6 0 208720 test.o/test.c里面,stack_dir和cpu_endian 以及*addr ,占用6字节. 384 24
26、60; 0 8 200 3050 usart.o/usart.c定义了一个串口接收数组buffer,占用200字节. - 1800
27、160; 278 336 20 2248 216735 Object Totals /总共2248+20字节 0
28、0; 0 32 0 0 0 (incl. Generated)
29、; 0 0 0 2 0 &
30、#160; 0 (incl. Padding)/2字节用于对其 - Code (inc. data) RO Data RW Data ZI Data Debug Library Member Name
31、160; 8 0 0 0 0 68 &
32、#160; _main.o 104 0 0 0 0
33、; 84 _printf.o 52 8 0 0 &
34、#160; 0 0 _scatter.o 26 0 0
35、60; 0 0 0 _scatter_copy.o 28 0
36、 0 0 0 0 _scatter_zi.o 48
37、; 6 0 0 0 96 _printf_char_common.o 3
38、6 4 0 0 0 80 _printf_cha
39、r_file.o 92 4 40 0 0
40、0; 88 _printf_hex_int.o 184 0 0 0
41、60; 0 88 _printf_intcommon.o 0 0 0
42、160; 0 0 0 _printf_percent.o 4 0
43、0; 0 0 0 0 _printf_percent_end.o 6
44、160; 0 0 0 0 0 _printf_x.o
45、0; 12 0 0 0 0 72
46、160; exit.o 8 0 0 0 0
47、60; 68 ferror.o 6 0 0 0 &
48、#160; 0 152 heapauxi.o 2 0 0
49、160; 0 0 0 libinit.o 2 0
50、 0 0 0 0 libinit2.o 2
51、60; 0 0 0 0 0 libshutdown.o
52、60; 2 0 0 0 0 0
53、60; libshutdown2.o 8 4 0 0 96
54、160; 68 libspace.o /库文件(printf使用),占用了96字节 24 4
55、160; 0 0 0 84 noretval_2printf.o 0
56、160; 0 0 0 0 0 rtentry.o 12
57、60; 0 0 0 0 0 rtentry2.o
58、 6 0 0 0 0
59、160; 0 rtentry4.o 2 0 0 0
60、; 0 0 rtexit.o 10 0 0
61、60; 0 0 0 rtexit2.o 74 0
62、 0 0 0 80 sys_stackheap_outer.o 2
63、60; 0 0 0 0 68 use_no_semi.o
64、160; 2 0 0 0 0 68 use_no
65、_semi_2.o 450 8 0 0 0
66、60; 236 faddsub_clz.o 388 76 0 0
67、0; 0 96 fdiv.o 62 4 0 0 &
68、#160; 0 84 ffixu.o 38 0 0
69、; 0 0 68 fflt_clz.o 258 4
70、; 0 0 0 84 fmul.o 140 4 &
71、#160; 0 0 0 84 fnaninf.o 10
72、60; 0 0 0 0 68 fretinf.o
73、 0 0 0 0 0 0
74、 usenofp.o - 2118 126 42 0 100
75、 1884 Library Totals /调用的库用了100字节. 10 0 2 0
76、; 4 0 (incl. Padding) /用于对其多占用了4个字节 - Code (inc. data) RO Data RW Data ZI Data Debug Library Name
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026秋季国家管网集团浙江省天然气管网有限公司高校毕业生招聘笔试备考试题(浓缩500题)及一套参考答案详解
- 2026年哈尔滨市农村信用社联合社秋季校园招聘笔试备考题库(浓缩500题)及答案详解参考
- 2026国网河北省电力公司高校毕业生提前批招聘笔试模拟试题浓缩500题附答案详解(巩固)
- 2026国网北京市高校毕业生提前批招聘(约450人)笔试模拟试题浓缩500题及答案详解(真题汇编)
- 2026国网甘肃省电力公司高校毕业生提前批招聘笔试参考题库浓缩500题附答案详解(培优)
- 2026秋季国家管网集团东北公司高校毕业生招聘笔试备考试题(浓缩500题)完整参考答案详解
- 2026秋季国家管网集团华中公司高校毕业生招聘笔试参考题库(浓缩500题)及答案详解(有一套)
- 2026国网天津市电力公司高校毕业生提前批招聘笔试模拟试题浓缩500题附答案详解(基础题)
- 2026秋季国家管网集团云南公司高校毕业生招聘笔试参考题库(浓缩500题)附答案详解(突破训练)
- 2025国网宁夏电力校园招聘(提前批)笔试模拟试题浓缩500题及答案详解(必刷)
- 2025年茶艺师职业技能考试试题含答案
- 游泳池恒温施工方案范本
- 吉林2025自考工商管理企业经营战略案例题专练
- 迎冬奥会滑雪活动策划方案
- 2025贵州盐业(集团)黔南有限责任公司公开招聘工作人员6人考试参考试题及答案解析
- 无人机理论培训课件
- 燃气管道工程信息化管理平台建设方案
- 新质生产力:从概念到实践的演进
- 2025版全新舞台租赁及演出项目管理合同
- 行政执法检查规范课件
- QC/T 983-2025汽车变速器总成清洁度检测方法
评论
0/150
提交评论