你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

详解STM32单片机的堆栈

[复制链接]
STMCU-管管 发布时间:2020-11-2 18:26
详解STM32单片机的堆栈

0 A- V$ W1 I9 e' u
9 B1 G7 c& r$ z学习STM32单片机的时候,总是能遇到“堆栈”这个概念。分享本文,希望对你理解堆栈有帮助。
2 p- m  w5 t, ]    对于了解一点汇编编程的人,就可以知道,堆栈是内存中一段连续的存储区域,用来保存一些临时数据。堆栈操作由PUSH、POP两条指令来完成。而程序内存可以分为几个区:( [3 S& z7 P( E+ t, R% P
栈区(stack)
$ b6 X0 M7 P/ s: r堆区(Heap)
2 @. P1 q+ B* \; h% B全局区(static)
! Z3 j" `, ^" r& t% D文字常亮区程序代码区: u0 v7 @2 r$ m, H: ]# m9 D, R
    程序编译之后,全局变量,静态变量已经分配好内存空间,在函数运行时,程序需要为局部变量分配栈空间,当中断来时,也需要将函数指针入栈,保护现场,以便于中断处理完之后再回到之前执行的函数。
7 |* {! S3 ?3 h1 i    栈是从高到低分配,堆是从低到高分配。
' o2 O. b6 M: }3 z普通单片机与STM32单片机中堆栈的区别) F6 L' x* {/ c& M3 Q0 D
    普通单片机启动时,不需要用bootloader将数据 从ROM搬移到RAM。
2 n% {1 v8 J1 ]) M9 ~" F    但是STM32单片机需要。
/ p: R; c* G  _    这里我们可以先看看单片机程序执行的过程,单片机执行分三个步骤:
& G1 L" {/ t  \0 J- N取执行# E/ \1 m6 l. Z* E
分析指令
$ [, `! I0 {. n执行指令
% `/ t' y: T" n. y$ `- R    根据PC的值从程序存储器读出指令,送到指令寄存器。然后分析执行执行。这样单片机就从内部程序存储器去代码指令,从RAM存取相关数据。
! Q' a3 W6 Z$ c9 K( T6 A8 P# Z6 V    RAM取数的速度是远高于ROM的,但是普通单片机因为本身运行频率不高,所以从ROM取指令慢并不影响。
! v" H5 W" c8 M- X9 a# X- G    而STM32的CPU运行的频率高,远大于从ROM读写的速度。所以需要用bootloader将数据 从ROM搬移到RAM。  `; S, |, s4 \9 b
    使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
# b- U1 Z9 \8 i' f    其实堆栈就是单片机中的一些存储单元,这些存储单元被指定保存一些特殊信息,比如地址(保护断点)和数据(保护现场)。) M& j1 ~, e8 J$ e& V
    如果非要给他加几个特点的话那就是:7 T+ T8 @6 l: G0 D: X5 k: Q: o" a
这些存储单元中的内容都是程序执行过程中被中断打断时,事故现场的一些相关参数。如果不保存这些参数,单片机执行完中断函数后就无法回到主程序继续执行了。2 X6 y9 S0 B3 N
这些存储单元的地址被记在了一个叫做堆栈指针(SP)的地方。
( q/ l; G: I& b; n) E结合STM32的开发讲述堆栈4 g9 ~6 c: S0 Q" W4 S( r) [5 Q) ~$ I
    从上面的描述可以看得出来,在代码中是如何占用堆和栈的。可能很多人还是无法理解,这里再结合STM32的开发过程中与堆栈相关的内容来进行讲述。
3 |1 c+ P( _8 [' E/ L% i    如何设置STM32的堆栈大小?: p5 E" S+ _+ E# H' N& P+ K% e  v% `0 ^
    在基于MDK的启动文件开始,有一段汇编代码是分配堆栈大小的。
2 ^6 d  T5 @% _! A: K  C# n 31.png
! A' W: l* \& r8 F7 S( {    这里重点知道堆栈数值大小就行。还有一段AREA(区域),表示分配一段堆栈数据段。数值大小可以自己修改,也可以使用STM32CubeMX数值大小配置,如下图所示。5 o2 W" G( V" W: ]# k# r
32.png
% F; T2 Y5 @, `  ]+ E2 Q' `    在IAR中,是通过工程配置堆栈大小,如下图所示。
4 e9 l1 j. u$ U- i! R1 | 33.png 3 M5 K) T- d1 Z1 h
    STM32F1默认设置值0x400,也就是1K大小。9 o9 X  J2 L  q) A0 V/ @
  1. Stack_Size EQU 0x400
复制代码
$ U& p+ g' E, L8 S9 C! d: i
7 v- z& c9 I1 ]3 A7 o# l  u
函数体内局部变量:
% P8 k4 ]0 L5 R+ M& a, {: t
  1. void Fun(void){ char i; int Tmp[256]; //...}
复制代码
7 J& n; m+ E# t

% M' D2 g. e' ~局部变量总共占用了256*4 + 1字节的栈空间。所以,在函数内有较多局部变量时,就需要注意是否超过我们配置的堆栈大小。
$ U% O1 n5 w) D! f. X3 U    函数参数:* d% G# ?- Y5 n# V. h! \

  1. 9 v- F6 ~/ H; T& s9 t- t+ P
  2. void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
复制代码
' G2 r& w6 ?" X  T. f7 Y

, w* C! D& y' `! L0 ?5 m  c 这里要强调一点:传递指针只占4字节,如果传递的是结构体,就会占用结构大小空间。提示:在函数嵌套,递归时,系统仍会占用栈空间。: b$ I* |3 U0 x
    堆(Heap)的默认设置0x200(512)字节。
( C2 S' d. k: Q1 c' b' L4 c

  1. ) z: k# r9 O5 ~) _( f+ }
  2. Heap_Size EQU 0x200
复制代码
& _, D! w; ^, H& s$ {" T
  y+ p5 t1 F$ M' T8 d
大部分人应该很少使用malloc来分配堆空间。虽然堆上的数据只要程序员不释放空间就可以一直访问,但是,如果忘记了释放堆内存,那么将会造成内存泄漏,甚至致命的潜在错误。" D7 q! y5 t, x; r' n
MDK中RAM占用大小分析) \) p2 V4 l& y
    经常在线调试的人,可能会分析一些底层的内容。这里结合MDK-ARM来分析一下RAM占用大小的问题。在MDK编译之后,会有一段RAM大小信息:
& C: D& N- {6 K0 A. A  |: I4 ?: `% n" H" T& R# d, H
    这里4+6=1640,转换成16进制就是0x668,在进行在调试时,会出现:+ d! R, e3 O0 _  t2 [6 B
9 H& }2 V1 b+ L  s/ {
    这个MSP就是主堆栈指针,一般我们复位之后指向的位置,复位执向的其实是栈顶:
4 g5 L/ H$ C5 p( u( R" p. H* j; Z 34.png 5 Y0 u$ g- [8 c
    而MSP指向地址0x20000668是0x20000000偏移0x668而得来。具体哪些地方占用了RAM,可以参看map文件中【Image Symbol Table】处的内容:
% h* s. z2 E& I6 ]4 W1 t 35.png : m3 s( v/ X% Z7 Y* e0 y* q/ u% I1 m* _
1 m% o5 A+ _% ?8 |4 z& |# `# {: e
# u+ U; g" m, }; `7 u' l
! }  G! Z- {! s) s8 y' Q
赞 收藏 评论0 发布时间:2020-11-2 18:26

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版