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

STM32 大小端模式 与 堆栈及其增长方向分析

[复制链接]
szc5b123 发布时间:2017-12-25 14:06
[转载] STM32 大小端模式 与 堆栈及其增长方向分析
http://www.openedv.com/thread-24152-1-1.html
(出处: OpenEdv-开源电子网)

. W; k3 i8 B- f. K
栈增长和大端/小端问题是和CPU相关的两个问题.% s- u) l3 U+ }" ?* |& I

1 l# V1 v) W+ N0 ]8 c+ `5 q1,首先来看:栈(STACK)的问题.4 C$ _  |  g" v' _5 j
2 f  [/ [) r  m  s0 d8 [6 T
函数的局部变量,都是存放在"栈"里面,栈的英文是:STACK.STACK的大小,我们可以在stm32的启动文件里面设置,以战舰stm32开发板为例,在startup_stm32f10x_hd.s里面,开头就有:1 V- Q/ c6 N; F4 l' |

0 n  g. X+ u6 d5 A- v+ FStack_Size      EQU     0x00000800# @0 N4 g9 P9 o* Y/ l, ?

7 S% h0 `3 g! r6 h! @$ J4 W( E表示栈大小是0X800,也就是2048字节.这样,CPU处理任务的时候,函数局部变量做多可占用的大小就是:2048字节,注意:是所有在处理的函数,包括函数嵌套,递归,等等,都是从这个"栈"里面,来分配的.* f1 W+ g$ Y8 m# I& g- Q' }( J( l( M
所以,如果一个函数的局部变量过多,比如在函数里面定义一个u8 buf[512],这一下就占了1/4的栈大小了,再在其他函数里面来搞两下,程序崩溃是很容易的事情,这时候,一般你会进入到hardfault....
: L" O. Q1 \; F9 r8 k这是初学者非常容易犯的一个错误.切记不要在函数里面放N多局部变量,尤其有大数组的时候!
4 S. U5 ]2 E# S2 r/ `, C
' X) w; }6 @8 Z- e% Z对于栈区,一般栈顶,也就是MSP,在程序刚运行的时候,指向程序所占用内存的最高地址.比如附件里面的这个程序序,内存占用如下图:
8 Y1 W. V, D; a+ }7 P+ Y, |9 y2 j
图中,我们可以看到,程序总共占用内存:20+2348字节=2368=0X9405 j& m, v' o- r1 l5 X' K5 Z6 j
那么程序刚开始运行的时候:MSP=0X2000 0000+0X940=0X2000 0940.
5 h+ d1 s( i9 Z! I; D9 o$ a事实上,也是如此,如图:

* ~* }" p6 |+ f$ N) j* B
4 ~$ S  E% D9 z8 C' S: `2 O
图中,MSP就是:0X2000 0940.
$ e" b  K1 ?/ a程序运行后,MSP就是从这个地址开始,往下给函数的局部变量分配地址.- b7 _; K$ L6 W1 U9 F! U

8 x5 s- }: g3 A再说说栈的增长方向,我们可以用如下代码测试: 3 l+ ?! t4 f  o9 D0 W2 Z4 \) @
! e, a: m5 r: h# R" K3 L$ ]
//保存栈增长方向
& r5 S: q7 @" Y/ t4 d( l/ `//0,向下增长;1,向上增长., @7 y6 N) N* r6 g
static u8 stack_dir;
1 c2 X/ X! x4 r7 H7 I
& L7 |) `: _. r" l* g2 Z7 l# N8 P//查找栈增长方向,结果保存在stack_dir里面.! \3 \$ @* J, J( ^
void find_stack_direction(void): k, `* U) d$ y* s  Q' w8 }' b
{
4 w' `# ^7 H6 z2 L1 z$ j# J2 J; \    static u8 *addr=NULL; //用于存放第一个dummy的地址。" m/ o2 e% n1 c. z9 |" i
    u8 dummy;               //用于获取栈地址
7 X$ Y# T7 G8 s; E+ y! P    if(addr==NULL)    //第一次进入. F8 ~. l5 I$ k: o3 \. _
    {                          * W" B9 M1 B9 @7 ?  O: t
        addr=&dummy;     //保存dummy的地址4 p3 O* [3 Y9 \7 |
        find_stack_direction ();  //递归 + |" a$ {/ g4 r
    }else                //第二次进入
" f9 z5 a, m1 b8 ^" t {  
* v/ _2 |( l0 V        if(&dummy>addr)stack_dir=1; //第二次dummy的地址大于第一次dummy,那么说明栈增长方向是向上的.
: S+ [8 Y5 s& A9 x8 u( v6 k( }        else stack_dir=0;           //第二次dummy的地址小于第一次dummy,那么说明栈增长方向是向下的.  
& L- G* F% ^5 S* `9 t; x }7 [  M$ v0 C4 u- e4 Y% x
}
9 V( D/ T* A/ w+ l8 R# G
. ]0 Q' {. o$ `+ d. @这个代码不是我写的,网上抄来的,思路很巧妙,利用递归,判断两次分配给dummy的地址,来比较栈是向下生长,还是向上生长.3 g, C- W8 M/ |3 u( o6 U2 z1 T
如果你在STM32测试这个函数,你会发现,STM32的栈,是向下生长的.事实上,一般CPU的栈增长方向,都是向下的.# e0 w" o2 u3 M3 E$ K, G% Z8 e: m- s
' T2 b3 s( v" R' N, l8 r
2,再来说说,堆(HEAP)的问题.
/ w5 y( h2 F9 T
# F/ G7 [' @0 d% _# W/ O0 v全局变量,静态变量,以及内存管理所用的内存,都是属于"堆"区,英文名:"HEAP"( }5 h0 J& f4 G. e9 B
与栈区不同,堆区,则从内存区域的起始地址,开始分配给各个全局变量和静态变量.! Q, i% C) r) [( i5 j) c
堆的生长方向,都是向上的.在程序里面,所有的内存分为:堆+栈. 只是他们各自的起始地址和增长方向不同,他们没有一个固定的界限,所以一旦堆栈冲突,系统就到了崩溃的时候了.# O: J- c& I6 H& s& h
同样,我们用附件里面的例程测试:9 L; ^" V: o* Y! U

9 F+ Q# ]. p" |( W& R
" ~3 Q! x; h  ?% h3 Istack_dir的地址是0X20000004,也就是STM32的内存起始端的地址.
7 b$ }: W& e' U; R' U这里本来应该是从0X2000 0000开始分配的,但是,我仿真发现0X2000 0000总是存放:0X2000 0398,这个值,貌似是MSP,但是又不变化,还请高手帮忙解释下.
2 Z! q+ u  s2 B( K9 [4 B其他的,全局变量,则依次递增,地址肯定大于0X20000004,比如cpu_endian的地址就是0X20000005.
6 n* e/ W" @7 p/ P/ ?这就是STM32内部堆的分配规则.* C7 `$ L& b3 E0 x" b

, C# s! {3 l6 h: t- m4 U3,再说说,大小端的问题.
& b' L( q  F: u大端模式:低位字节存在高地址上,高位字节存在低地址上
% y/ v( ~, r, g* h小端模式:高位字节存在高地址上,低位字节存在低地址上
+ O$ O0 L) V* {: ]1 q
8 f- G' z% a# Z& x/ CSTM32属于小端模式,简单的说,比如u32 temp=0X12345678;
) F2 l6 P; \" G; r  q假设temp地址在0X2000 0010.4 q+ z7 U* |: D6 r+ k, a/ a, p! m  D
那么在内存里面,存放就变成了:
  r3 o8 ?! q; }* O. V1 J( I# x地址              |            HEX         |0 M9 q- p' l7 @4 m0 Y, l; k( i7 ]7 Z9 ~
0X2000 0010  |  78   56   43  12  |0 b9 ]& x  ~2 [6 N
  o5 ^3 f; {1 ~. f
CPU到底是大端还是小端,可以通过如下代码测试:
7 u) \7 l2 x4 T% Z1 V7 n//CPU大小端
8 p$ H  ^0 s0 K7 S//0,小端模式;1,大端模式.
/ d; H: m9 e  I. |) W& }static u8 cpu_endian;
  H' |8 O1 C$ v! J( ?: _( G2 p6 k+ w! ^6 H0 j# G
//获取CPU大小端模式,结果保存在cpu_endian里面4 J) k7 `0 D; h8 w) J8 C
void find_cpu_endian(void)
2 M- [" K$ U. R6 l{ % u/ I, W: f. q5 ]% m% {
int x=1;! ^# Z" }' d; J: z  g! F
if(*(char*)&x==1)cpu_endian=0; //小端模式
; ~( P3 z' W/ Z! v$ K, s7 J% C else cpu_endian=1;    //大端模式  
' s/ |+ U" [% w% U! A}' U2 n% f* X0 _3 w
以上测试,在STM32上,你会得到cpu_endian=0,也就是小端模式.
: @0 J5 m- V5 A; Q
) x6 X" R. B) J$ Z
4 a! k! h( R' n3,最后说说,STM32内存的问题.
; z& ?% E# _6 j" G6 F% J" R: o" k8 @    还是以附件工程为例,在前面第一个图,程序总共占用内存:20+2348字节,这么多内存,到底是怎么得来的呢?
1 `2 g% {' k; I4 B  x我们可以双击Project侧边栏的:Targt1,会弹出test.map,在这个里面,我们就可以清楚的知道这些内存到底是怎么来的了.在这个test.map最后,Image 部分有:
/ M& M6 ~: F8 z==============================================================================
Image component sizes
/ o/ H9 B6 a0 Y! N
      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name
       172         10          0          4          0        995   delay.o//delay.c里面,fac_us和fac_ms,共占用4字节; s# Y: p" D  L0 c6 l
       112         12          0          0          0        427   led.o0 X) b" Y1 g# U% j) u
        72         26        304          0       2048        828   startup_stm32f10x_hd.o  //启动文件,里面定义了Stack_Size为0X800,所以这里是2048.3 d  @; ~4 p0 P
       712         52          0          0          0       2715   sys.o
  F! S0 x9 w$ l" V. D       348        154          0          6          0     208720   test.o//test.c里面,stack_dir和cpu_endian 以及*addr  ,占用6字节.
' V7 E& w2 A0 u* P0 y# j1 V       384         24          0          8        200       3050   usart.o//usart.c定义了一个串口接收数组buffer,占用200字节.
    ----------------------------------------------------------------------9 h8 @* d2 L, a5 _* p+ S$ Q. M1 f% u
      1800        278        336         20       2248     216735   Object Totals //总共2248+20字节
' h/ ?9 g+ C2 c5 G         0          0         32          0          0          0   (incl. Generated)( q' W) V$ J6 o1 W+ y9 M
         0          0          0          2          0          0   (incl. Padding)//2字节用于对其
    ----------------------------------------------------------------------
      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Library Member Name
         8          0          0          0          0         68   __main.o
  \  B: ]) F! y4 ^, z; O       104          0          0          0          0         84   __printf.o
# d" i& d. A+ p8 {' e$ b2 l8 o# j        52          8          0          0          0          0   __scatter.o
# N' x& F$ D) r; v; b' g  }        26          0          0          0          0          0   __scatter_copy.o6 `) E8 T9 E$ O1 `6 {' v7 }; C& |4 R
        28          0          0          0          0          0   __scatter_zi.o- ]  h( N9 {  f8 D; L, p5 q6 j( Y7 B
        48          6          0          0          0         96   _printf_char_common.o
1 r9 }7 T; M  m& P        36          4          0          0          0         80   _printf_char_file.o
  w5 Y" Q; F; `+ Y+ i        92          4         40          0          0         88   _printf_hex_int.o5 r/ F! n& V/ l/ j" C
       184          0          0          0          0         88   _printf_intcommon.o0 |5 W6 k" {' J( f. I1 l
         0          0          0          0          0          0   _printf_percent.o
4 M$ G6 M, G- S2 P: ~2 G+ `7 T         4          0          0          0          0          0   _printf_percent_end.o
: F. [- z( n. P  r         6          0          0          0          0          0   _printf_x.o
& D1 C4 ?% f* s& n; j        12          0          0          0          0         72   exit.o
2 w; V  G- H: A2 l9 p         8          0          0          0          0         68   ferror.o
6 u7 U# W' M9 y. E- B. z1 J# P         6          0          0          0          0        152   heapauxi.o) ~# _) L) I, j; R6 {
         2          0          0          0          0          0   libinit.o" t! R: \: ~5 k) M& c1 F) Y3 U, v
         2          0          0          0          0          0   libinit2.o
  a$ c. K+ R' Z         2          0          0          0          0          0   libshutdown.o8 Z0 ?( o4 c% c8 V0 n2 B7 t3 ^( u
         2          0          0          0          0          0   libshutdown2.o0 ~  a: P) }" d) U# K
         8          4          0          0         96         68   libspace.o          //库文件(printf使用),占用了96字节
7 ?9 y0 W/ k9 _& H9 N/ Y        24          4          0          0          0         84   noretval__2printf.o. K* f1 w' X2 Q$ Q9 V
         0          0          0          0          0          0   rtentry.o
3 k# [* u! J8 S) s( c        12          0          0          0          0          0   rtentry2.o5 Q2 B4 {& O. T! R+ E# w
         6          0          0          0          0          0   rtentry4.o3 ]3 p; E" J% m; F: d! y9 o
         2          0          0          0          0          0   rtexit.o
/ z  {( c* |5 I7 {        10          0          0          0          0          0   rtexit2.o
0 ^' K4 i' O, T$ G9 {! T* S9 b% _        74          0          0          0          0         80   sys_stackheap_outer.o; h0 o  K  n6 J) `& o* V* g
         2          0          0          0          0         68   use_no_semi.o
+ k$ f& ~1 N4 Q         2          0          0          0          0         68   use_no_semi_2.o
  Z4 S; q" e: T  ^8 W       450          8          0          0          0        236   faddsub_clz.o
3 t2 K* B  U9 V% w* d- P  D       388         76          0          0          0         96   fdiv.o
9 |6 L0 U5 V, D1 r. M* T        62          4          0          0          0         84   ffixu.o
9 g! m  W! f/ a5 t        38          0          0          0          0         68   fflt_clz.o
1 Y7 [# `' J0 g       258          4          0          0          0         84   fmul.o
; C, _; a% h4 {; w. y       140          4          0          0          0         84   fnaninf.o
. b1 J% S5 D5 s( _- j- w! x. ?        10          0          0          0          0         68   fretinf.o  n' }" I, k. ]6 S) N/ t) \
         0          0          0          0          0          0   usenofp.o
    ----------------------------------------------------------------------
: N8 a9 l6 p, l5 r* _7 `3 X      2118        126         42          0        100       1884   Library Totals  //调用的库用了100字节.
6 F3 ?* z1 b- Z& V" ~        10          0          2          0          4          0   (incl. Padding)   //用于对其多占用了4个字节
    ----------------------------------------------------------------------
      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Library Name
       762         30         40          0         96       1164   c_w.l
: k9 E- \: r6 N2 F. k$ ~      1346         96          0          0          0        720   fz_ws.l
    ----------------------------------------------------------------------
; o8 ]$ D, i. T, k) C, [0 Z5 X      2118        126         42          0        100       1884   Library Totals
    ----------------------------------------------------------------------
==============================================================================

9 E. d3 q; n& n! f& F# s6 |# l( S      Code (inc. data)   RO Data    RW Data    ZI Data      Debug  
      3918        404        378         20       2348     217111   Grand Totals
* \( \$ T/ G$ U7 P3 x      3918        404        378         20       2348     217111   ELF Image Totals
" c  ?8 |) {& {0 y      3918        404        378         20          0          0   ROM Totals
==============================================================================
    Total RO  Size (Code + RO Data)                 4296 (   4.20kB)
4 P5 \. t( X6 [6 E0 S4 G( f    Total RW  Size (RW Data + ZI Data)              2368 (   2.31kB)   //总共占用:2248+20+100=2368.
% B3 o/ Z' J0 F6 u    Total ROM Size (Code + RO Data + RW Data)       4316 (   4.21kB)
==============================================================================. j% m) y' _: v: x* |4 i4 V0 L

* C* y  m9 Y( m通过这个文件,我们就可以分析整个内存,是怎么被占用的,具体到每个文件,占用多少.一目了然了.9 {% h/ b; E& G% X: P3 ~) p3 D

( C( q$ c% T6 I# R4,最后,看看整个测试代码:  v' i) U) E( k  |) x: C0 Q6 U
main.c代码如下,工程见附件.3 [" G: J! _' E" z
#include "sys.h"
( c/ m* t4 O* k% i6 Z& m# r4 U% @3 e#include "usart.h"  
  n; O4 y* h/ G% H, w#include "delay.h"
$ B% t/ `% @7 j% R#include "led.h"
0 l8 d# J4 f* ^$ k#include "beep.h"    % U2 W" q  ~% q) r
#include "key.h"   
* ^/ I4 v8 ~6 H+ s/ z6 G; A//ALIENTEK战舰STM32开发板堆栈增长方向以及CPU大小端测试
//保存栈增长方向
2 P8 p# o, T$ l2 I8 K. L//0,向下增长;1,向上增长.
) {- g. D% f6 j6 b& O5 z1 Ostatic u8 stack_dir;
//CPU大小端
+ R: N, y3 U' F; |//0,小端模式;1,大端模式.
' ]8 l# l' h5 j0 Istatic u8 cpu_endian;
/ R% _) k# q% u0 n% i. I7 E

- k+ m- s( X' `5 W8 E//查找栈增长方向,结果保存在stack_dir里面.% v! j! Y; P8 G, J0 W
void find_stack_direction(void)  q- j+ x4 o* y% ~& I5 O3 q2 d
{' e! c8 T6 @# Z. A% u
    static u8 *addr=NULL; //用于存放第一个dummy的地址。( D0 G: |  v1 f: D; q
    u8 dummy;               //用于获取栈地址 " c" Q. e* J, A! J6 V- ], q* E
    if(addr==NULL)    //第一次进入5 h/ P1 H+ s! i; {0 C3 l4 m
    {                          
$ Q9 ^4 A9 L, A- @) r        addr=&dummy;     //保存dummy的地址
) \( Y/ w% _: V, v4 E' n9 o! v6 ]  y        find_stack_direction ();  //递归
6 w, t; O# D$ D* M' x2 q" \    }else                //第二次进入
( t4 }5 A2 |4 J+ R; ` {  
6 U4 r7 k' e2 a4 `+ T6 P        if(&dummy>addr)stack_dir=1; //第二次dummy的地址大于第一次dummy,那么说明栈增长方向是向上的. 2 U0 Z: Z" g- ?6 K
        else stack_dir=0;           //第二次dummy的地址小于第一次dummy,那么说明栈增长方向是向下的.  
3 T$ h# W$ X6 t$ w) K }- _( G9 b3 j, F+ J& k! f& u' s  g
} . A# Z0 w! l: l7 ?8 O. U
//获取CPU大小端模式,结果保存在cpu_endian里面
9 R. E8 ~& ^& v7 u6 zvoid find_cpu_endian(void)1 U9 f: m3 |7 C# ~* o) j3 @
{ " D1 f3 F" |( l7 l
int x=1;; V& }: V7 s3 T/ {
if(*(char*)&x==1)cpu_endian=0; //小端模式
  s: e, J# r; ]' d- m2 J$ Q else cpu_endian=1;    //大端模式  $ U( b4 O5 r4 P2 p$ |0 p' S! }# y$ Q
}
9 {/ O% E% J$ y5 R2 I+ J$ ]9 aint main(void)$ L% C3 O9 D/ P$ s
{   
. m4 t3 l4 [- n# y! } Stm32_Clock_Init(9); //系统时钟设置
2 {6 p, l  {: v. H! h1 e# u, f3 t uart_init(72,9600);   //串口初始化为9600
- G5 O1 S- E* A% g delay_init(72);       //延时初始化
( M8 Q% u/ C9 a! V LED_Init();      //初始化与LED连接的硬件接口  9 Y% y$ P" ~! a) b' V- [
    printf("stack_dir:%x\r\n",&stack_dir);7 N# U" @5 M9 X! I
    printf("cpu_endian:%x\r\n",&cpu_endian);5 B8 ~2 O0 @, x/ m- M% A: g
6 m/ b6 ]6 R# e% B1 K
find_stack_direction(); //获取栈增长方式
/ D# C, Q& v4 n& R3 x/ B! E find_cpu_endian();  //获取CPU大小端模式* o9 h+ A/ v7 ~" W
  while(1)
: W; m1 a; k+ _ {- U" p+ M5 @3 J# `4 _8 s5 |# b2 e
  if(stack_dir)printf("STACK DIRCTION:向上生长\r\n\r\n");
5 b# u3 |0 y6 {# E3 }# }2 y9 K, h  else printf("STACK DIRCTION:向下生长\r\n\r\n");" P* z! |2 \9 H1 U- p
  if(cpu_endian)printf("CPU ENDIAN:大端模式\r\n\r\n");9 t2 H& n7 S5 t. l
  else printf("CPU ENDIAN:小端模式\r\n\r\n"); 1 y5 C! P$ E' B' z7 m
  delay_ms(500);& |( ?) Z6 K" C  k
  LED0=!LED0;  3 p. b; c. H. S1 f: {7 w
}  * |  \3 H  W3 e. ?
}+ [9 i+ X$ h3 S8 t
测试结果如图:0 e. k4 s( r& q& h" `6 e7 b/ L
, t+ z/ v" s* D& R# E) D4 ^
收藏 1 评论0 发布时间:2017-12-25 14:06

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版