学习STM32单片机,要理解它的堆栈

学习stm32单片机的时候,总是能遇到 “堆栈” 这个概念。对于了解一点汇编编程的人,就可以知道,堆栈是内存中一段连续的存储区域,用来保存一些临时数据。堆栈操作由 push、pop 两条指令来完成。而程序内存可以分为几个区:栈区(stack)、堆区(heap)、全局区(static)、字符常量区、程序代码区。
栈,用于存放局部变量,局部数组等; 堆,用于malloc申请内存空间;  全局静态区,用于保存全局变量和静态变量;  字符常量区,用于保存字符串等;  代码区,用于保存程序的二进制代码。
程序编译之后,全局变量,静态变量已经分配好内存空间。在函数运行时,程序需要为局部变量分配栈空间,当中断来时,也需要将函数指针入栈,保护现场,以便于中断处理完之后再回到之前执行的函数。栈是从高到低分配,堆是从低到高分配。
普通单片机与stm32单片机中堆栈的区别
普通单片机启动时,不需要用bootloader 将代码从rom搬移到ram;但是stm32单片机需要。这里我们可以先看看单片机程序执行的过程,单片机执行分三个步骤:取指令、分析指令、执行指令。根据pc的值从程序存储器读出指令,送到指令寄存器。然后分析、执行。这样单片机就从内部程序存储器取代码指令,从ram 存取相关数据。ram 取数据的速度是远高于 rom 的,但是普通单片机因为本身运行频率不高,所以从 rom 取指令慢并不影响。
而stm32的cpu运行的频率高,远大于从rom读写的速度。所以需要用 bootloader 将代码从rom搬移到 ram(请注意,这部分表述不准确)。使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。   其实堆栈就是单片机中的一些存储单元,这些存储单元被指定保存一些特殊信息,比如地址(保护断点)和数据(保护现场)。如果非要给堆栈加几个特点的话,那就是:
这些存储单元中的内容都是程序执行过程中被中断打断时,事故现场的一些相关参数。如果不保存这些参数,单片机执行完中断函数后就无法回到主程序继续执行了。
这些存储单元的地址被记在了一个叫做堆栈指针(sp)的地方。
结合stm32的开发讲述堆栈
从上面的描述可以看得出来,在代码中是如何占用堆和栈的。可能很多人还是无法理解,这里再结合stm32的开发过程中与堆栈相关的内容来进行讲述。  如何设置stm32的堆栈大小?在基于mdk的启动文件开始,有一段汇编代码是分配堆栈大小的。
这里重点知道堆栈数值大小就行。还有一段 area(区域),表示分配一段堆栈数据段。数值大小可以自己修改,也可以使用stm32cubemx数值大小配置,如下图所示。
stm32f1默认设置值 0x400,也就是1k大小。
stack_size equ 0x400函数体内局部变量:void fun(void) { char i; int tmp[256]; //... }局部变量总共占用了256*4+1字节的栈空间。所以,在函数内有较多局部变量时,就需要注意是否超过我们配置的堆栈大小。 函数参数:void hal_gpio_init(gpio_typedef *gpiox, gpio_inittypedef *gpio_init)要强调一点:传递指针只占4字节,如果传递的是结构体,就会占用结构大小空间。提示:在函数嵌套,递归时,系统仍会占用栈空间。 堆(heap)的默认设置 0x200(512)字节。heap_size equ 0x200大部分人应该很少使用malloc来分配堆空间。虽然堆上的数据只要程序员不释放空间就可以一直访问,但是,如果忘记了释放堆内存,那么将会造成内存泄漏,甚至致命的潜在错误。
mdk中ram占用大小分析
经常在线调试的人,可能会分析一些底层的内容。这里结合mdk-arm来分析一下ram 占用大小的问题。在mdk编译之后,会有一段 ram大小信息:
这里4+1636=1640,转换成16进制就是0x668,在线进行调试时,会出现:
这个msp就是主堆栈指针,一般我们复位之后指向的位置,复位指向的其实是栈顶:
而msp指向地址0x20000668是0x20000000偏移0x668 而得来。具体哪些地方占用了ram,可以参看map文件中【image symbol table】处的内容:
stm32栈空间溢出的处理方法
在写stm32程序时会用到一些局部变量,函数中的局部变量是存在栈空间当中,在
stm32的启动文件当中可以设置栈空间大小。如果在函数当中定义的局部变量超过栈空间大小编译时不会报错,但运行时极有可能出现错误,甚至会导致程序卡死,那么该如何处理呢?
方法一:修改栈空间大小1、找到启动文件hd.s (例如:是stm32f103 则是 startup_stm32f10x_hd.s) 2、找到启动文件当中的栈空间大小定义:
//系统默认栈空间大小为 1k stack_size   equ    0x000004003、修改栈空间大小//我们修改为4k stack_size   equ    0x00001000方法二:将局部变量改为全局变量。

自动驾驶将在全新的十年中飞速发展
便携式气象站设备的应用、参数及性能
μC/OS-Ⅱ操作系统设备驱动设计及实际应用举例
利用Arduino制作8×10 LED矩阵
选择完整的交钥匙PCB组装对于企业来说有什么好处
学习STM32单片机,要理解它的堆栈
atmel-arm9芯片特点
亚马逊第二代智能音箱Echo Show评测汇总:非常成功 或将大有所为
为应用寻找一个合适的“Goldilocks”电压基准
高通骁龙888跨越式命名背后蕴藏着何种野心?
华宇电子亮相2022世界集成电路大会
华为mate9和p9的区别,16nm工艺处理器给力升级但mate9曲屏版或跳票
C语言零基础项目:生命游戏!详细思路+源码分享
基于PLC的机器人伺服运动控制系统设计详解
用MDaemon搭建邮件服务器-设置篇
松下进一步增强加码动力电池业务的决心
谷歌发布第三季度财报,总营收461.73亿美元
华为 P20在上海发布 还有全球首款全面屏笔记本
怎么应对刘海屏机型的海报设计
相控阵雷达性能的基石:宽禁带半导体