对于稍微懂一点汇编编程的人来说,可以知道栈是内存中的一块连续的存储区域,用来保存一些临时数据。 堆栈操作由PUSH和POP两条指令完成。 程序存储器可以分为几个区域:
程序编译完成后,已经为全局变量和静态变量分配了内存空间。 函数运行时,程序需要为局部变量分配堆栈空间。 当中断到来时,也需要将函数指针压入堆栈以保护场景,以便完成中断处理。 然后返回之前执行的函数。
栈是从高位到低位分配的,堆是从低位到高位分配的。
普通单片机和STM32单片机中堆栈的区别
当普通微控制器启动时,无需使用引导加载程序将代码从 ROM 移动到 RAM。
但STM32微控制器需要它。
这里我们可以先看一下单片机程序执行的流程。 单片机执行分为三步:
根据PC的值从程序存储器中读取指令并发送给指令寄存器。 然后分析并执行执行。 通过这种方式,微控制器从内部程序存储器中检索代码指令并从RAM中访问相关数据。
RAM取数据的速度比ROM高很多,但普通单片机的工作频率不高,所以从ROM取指令的慢并不影响它。
STM32 CPU运行频率很高,比ROM读写速度快很多。 因此,您需要使用引导加载程序将代码从 ROM 移动到 RAM。
使用堆栈就像我们去餐馆吃饭一样。 我们只是点餐(发出申请)、付款、吃饭(使用)。 当我们吃饱了,我们就离开。 我们不用操心切菜、洗菜、洗碗、洗锅等准备工作。 完成工作的优点是速度快,但自由度不大。 使用 Dui 就像制作自己喜欢的菜肴一样。 比较麻烦,但是更符合自己的口味,自由度更大。
其实堆栈就是单片机中的一些存储单元。 这些存储单元被指定用于存储一些特殊信息,例如地址(保护断点)和数据(保护站点)。
如果我必须为他添加一些功能,它们将是:
基于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个字节。 如果传递一个结构体,它将占用结构体大小的空间。 提示:当函数嵌套和递归时,系统仍然会占用堆栈空间。
堆的默认设置是 0x200 (512) 字节。
Heap_Size EQU 0x200
大多数人应该很少使用 malloc 来分配堆空间。 虽然只要程序员不释放空间就可以访问堆上的数据,但是如果忘记释放堆内存,就会导致内存泄漏甚至致命的潜在错误。
MDK中RAM使用情况分析
经常在线调试的人可能会分析一些底层的内容。 这里我们结合MDK-ARM来分析RAM占用问题。 MDK编译后,会有一条RAM大小信息:
这里4+6=1640,换算成十六进制就是0x668。 调试的时候会出现:
该 MSP 是主堆栈指针。 一般指向复位后的位置。 重置指向栈顶:
MSP指向地址0x20000668,该地址是从0x20000000偏移0x668得出的。 具体占用RAM的地方可以参考map文件中【图像符号表】的内容: