历史上的今天
返回首页

历史上的今天

今天是:2024年09月08日(星期日)

2018年09月08日 | STM32 堆栈大小详解 以及变量存储位置

2018-09-08 来源:eefocus

栈增长和大端/小端问题是和CPU相关的两个问题.

1,首先来看:栈(STACK)的问题.

函数的局部变量,都是存放在"栈"里面,栈的英文是:STACK.STACK的大小,我们可以在stm32的启动文件里面设置,以战舰stm32开发板为例,在startup_stm32f10x_hd.s里面,开头就有:

Stack_Size      EQU     0x00000800

表示栈大小是0X800,也就是2048字节.这样,CPU处理任务的时候,函数局部变量做多可占用的大小就是:2048字节,注意:是所有在处理的函数,包括函数嵌套,递归,等等,都是从这个"栈"里面,来分配的.
所以,如果一个函数的局部变量过多,比如在函数里面定义一个u8 buf[512],这一下就占了1/4的栈大小了,再在其他函数里面来搞两下,程序崩溃是很容易的事情,这时候,一般你会进入到hardfault....
这是初学者非常容易犯的一个错误.切记不要在函数里面放N多局部变量,尤其有大数组的时候!

对于栈区,一般栈顶,也就是MSP,在程序刚运行的时候,指向程序所占用内存的最高地址.比如附件里面的这个程序序,内存占用如下图:


图中,我们可以看到,程序总共占用内存: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=NULL; //用于存放第一个dummy的地址。
    u8 dummy;               //用于获取栈地址 
    if(addr==NULL)    //第一次进入
    {                          
        addr=&dummy;     //保存dummy的地址
        find_stack_direction ();  //递归 
    }else                //第二次进入 
 {  
        if(&dummy>addr)stack_dir=1; //第二次dummy的地址大于第一次dummy,那么说明栈增长方向是向上的. 
        else stack_dir=0;           //第二次dummy的地址小于第一次dummy,那么说明栈增长方向是向下的.  
 }


这个代码不是我写的,网上抄来的,思路很巧妙,利用递归,判断两次分配给dummy的地址,来比较栈是向下生长,还是向上生长.
如果你在STM32测试这个函数,你会发现,STM32的栈,是向下生长的.事实上,一般CPU的栈增长方向,都是向下的.

2,再来说说,堆(HEAP)的问题.

全局变量,静态变量,以及内存管理所用的内存,都是属于"堆"区,英文名:"HEAP"
与栈区不同,堆区,则从内存区域的起始地址,开始分配给各个全局变量和静态变量.
堆的生长方向,都是向上的.在程序里面,所有的内存分为:堆+栈. 只是他们各自的起始地址和增长方向不同,他们没有一个固定的界限,所以一旦堆栈冲突,系统就到了崩溃的时候了.
同样,我们用附件里面的例程测试:



stack_dir的地址是0X20000004,也就是STM32的内存起始端的地址.
这里本来应该是从0X2000 0000开始分配的,但是,我仿真发现0X2000 0000总是存放:0X2000 0398,这个值,貌似是MSP,但是又不变化,还请高手帮忙解释下.
其他的,全局变量,则依次递增,地址肯定大于0X20000004,比如cpu_endian的地址就是0X20000005.
这就是STM32内部堆的分配规则.

3,再说说,大小端的问题.
大端模式:低位字节存在高地址上,高位字节存在低地址上 
小端模式:高位字节存在高地址上,低位字节存在低地址上

STM32属于小端模式,简单的说,比如u32 temp=0X12345678;
假设temp地址在0X2000 0010.
那么在内存里面,存放就变成了:
地址              |            HEX         |
0X2000 0010  |  78   56   43  12  |

CPU到底是大端还是小端,可以通过如下代码测试:
//CPU大小端
//0,小端模式;1,大端模式.
static u8 cpu_endian;

//获取CPU大小端模式,结果保存在cpu_endian里面
void find_cpu_endian(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,会弹出test.map,在这个里面,我们就可以清楚的知道这些内存到底是怎么来的了.在这个test.map最后,Image 部分有:
==============================================================================

Image component sizes


      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字节
       112         12          0          0          0        427   led.o
        72         26        304          0       2048        828   startup_stm32f10x_hd.o  //启动文件,里面定义了Stack_Size为0X800,所以这里是2048.
       712         52          0          0          0       2715   sys.o
       348        154          0          6          0     208720   test.o//test.c里面,stack_dir和cpu_endian 以及*addr  ,占用6字节.
       384         24          0          8        200       3050   usart.o//usart.c定义了一个串口接收数组buffer,占用200字节.

    ----------------------------------------------------------------------
      1800        278        336         20       2248     216735   Object Totals //总共2248+20字节
         0          0         32          0          0          0   (incl. Generated)
         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
       104          0          0          0          0         84   __printf.o
        52          8          0          0          0          0   __scatter.o
        26          0          0          0          0          0   __scatter_copy.o
        28          0          0          0          0          0   __scatter_zi.o
        48          6          0          0          0         96   _printf_char_common.o
        36          4          0          0          0         80   _printf_char_file.o
        92          4         40          0          0         88   _printf_hex_int.o
       184          0          0          0          0         88   _printf_intcommon.o
         0          0          0          0          0          0   _printf_percent.o
         4          0          0          0          0          0   _printf_percent_end.o
         6          0          0          0          0          0   _printf_x.o
        12          0          0          0          0         72   exit.o
         8          0          0          0          0         68   ferror.o
         6          0          0          0          0        152   heapauxi.o
         2          0          0          0          0          0   libinit.o
         2          0          0          0          0          0   libinit2.o
         2          0          0          0          0          0   libshutdown.o
         2          0          0          0          0          0   libshutdown2.o
         8          4          0          0         96         68   libspace.o          //库文件(printf使用),占用了96字节
        24          4          0          0          0         84   noretval__2printf.o
         0          0          0          0          0          0   rtentry.o
        12          0          0          0          0          0   rtentry2.o
         6          0          0          0          0          0   rtentry4.o
         2          0          0          0          0          0   rtexit.o
        10          0          0          0          0          0   rtexit2.o
        74          0          0          0          0         80   sys_stackheap_outer.o
         2          0          0          0          0         68   use_no_semi.o
         2          0          0          0          0         68   use_no_semi_2.o
       450          8          0          0          0        236   faddsub_clz.o
       388         76          0          0          0         96   fdiv.o
        62          4          0          0          0         84   ffixu.o
        38          0          0          0          0         68   fflt_clz.o
       258          4          0          0          0         84   fmul.o
       140          4          0          0          0         84   fnaninf.o
        10          0          0          0          0         68   fretinf.o
         0          0          0          0          0          0   usenofp.o

    ----------------------------------------------------------------------
      2118        126         42          0        100       1884   Library Totals  //调用的库用了100字节.
        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
      1346         96          0          0          0        720   fz_ws.l

    ----------------------------------------------------------------------
      2118        126         42          0        100       1884   Library Totals

    ----------------------------------------------------------------------

==============================================================================


      Code (inc. data)   RO Data    RW Data    ZI Data      Debug  

      3918        404        378         20       2348     217111   Grand Totals
      3918        404        378         20       2348     217111   ELF Image Totals
      3918        404        378         20          0          0   ROM Totals

==============================================================================

    Total RO  Size (Code + RO Data)                 4296 (   4.20kB)
    Total RW  Size (RW Data + ZI Data)              2368 (   2.31kB)   //总共占用:2248+20+100=2368.
    Total ROM Size (Code + RO Data + RW Data)       4316 (   4.21kB)

==============================================================================

通过这个文件,我们就可以分析整个内存,是怎么被占用的,具体到每个文件,占用多少.一目了然了.

4,最后,看看整个测试代码:
main.c代码如下,工程见附件.
#include "sys.h"
#include "usart.h"  
#include "delay.h" 
#include "led.h" 
#include "beep.h"    
#include "key.h"    
//ALIENTEK战舰STM32开发板堆栈增长方向以及CPU大小端测试

//保存栈增长方向
//0,向下增长;1,向上增长.
static u8 stack_dir;

//CPU大小端
//0,小端模式;1,大端模式.
static u8 cpu_endian;

 


//查找栈增长方向,结果保存在stack_dir里面.
void find_stack_direction(void)
{
    static u8 *addr=NULL; //用于存放第一个dummy的地址。
    u8 dummy;               //用于获取栈地址 
    if(addr==NULL)    //第一次进入
    {                          
        addr=&dummy;     //保存dummy的地址
        find_stack_direction ();  //递归 
    }else                //第二次进入 
 {  
        if(&dummy>addr)stack_dir=1; //第二次dummy的地址大于第一次dummy,那么说明栈增长方向是向上的. 
        else stack_dir=0;           //第二次dummy的地址小于第一次dummy,那么说明栈增长方向是向下的.  
 }

//获取CPU大小端模式,结果保存在cpu_endian里面
void find_cpu_endian(void)

 int x=1;
 if(*(char*)&x==1)cpu_endian=0; //小端模式 
 else cpu_endian=1;    //大端模式  

int main(void)
{    
 Stm32_Clock_Init(9); //系统时钟设置
 uart_init(72,9600);   //串口初始化为9600
 delay_init(72);       //延时初始化 
 LED_Init();      //初始化与LED连接的硬件接口  
    printf("stack_dir:%x\r\n",&stack_dir);
    printf("cpu_endian:%x\r\n",&cpu_endian);
 
 find_stack_direction(); //获取栈增长方式
 find_cpu_endian();  //获取CPU大小端模式
  while(1)
 {
  if(stack_dir)printf("STACK DIRCTION:向上生长\r\n\r\n");
  else printf("STACK DIRCTION:向下生长\r\n\r\n");
  if(cpu_endian)printf("CPU ENDIAN:大端模式\r\n\r\n");
  else printf("CPU ENDIAN:小端模式\r\n\r\n"); 
  delay_ms(500);
  LED0=!LED0;  
 }  
}
测试结果如图:
 


推荐阅读

史海拾趣

Dymec公司的发展小趣事

随着全球对环保问题的日益关注,Dymec公司也积极响应号召,开始推动绿色环保的转型。公司投入大量资金研发环保型电子连接器产品,采用环保材料和生产工艺,减少了对环境的污染。这一转型不仅提升了公司的社会责任感,也为公司赢得了更多客户的青睐。

海芯科技(AVIA)公司的发展小趣事

为了进一步拓展业务和提升公司的竞争力,海芯科技积极寻求与行业内外的合作伙伴建立战略合作关系。通过与上下游企业的紧密合作,海芯科技成功实现了产业链的整合和优化,进一步提升了产品的质量和性能。同时,公司还通过与国际知名企业的合作,将先进的技术和管理经验引入到公司内部,为公司的发展注入了新的活力。

General Industrial Controls ( GIC )公司的发展小趣事
具有完善的保护电路,包括电流限制和热关断电路。
Fillfactory Nv公司的发展小趣事

FIDELIX公司成立于1990年,是一家专注于存储芯片研发与销售的韩国企业。在公司成立初期,FIDELIX凭借对技术的深入研究和对市场的敏锐洞察,成功推出了多款性能优越的NAND FLASH(闪存)和SDR/DDR(单/双数率同步动态存储器)等存储芯片产品。这些产品不仅在韩国市场上获得了良好的口碑,同时也开始逐步进入国际市场。

随着技术的不断进步和市场的不断扩大,FIDELIX逐渐在韩国存储芯片领域崭露头角。公司不断投入研发资金,加强技术创新,努力提升产品的性能和品质。同时,FIDELIX也积极拓展销售渠道,与多家国内外知名厂商建立了合作关系,为公司的持续发展奠定了坚实的基础。

Cliff Electronic Components公司的发展小趣事

FIDELIX公司成立于1990年,是一家专注于存储芯片研发与销售的韩国企业。在公司成立初期,FIDELIX凭借对技术的深入研究和对市场的敏锐洞察,成功推出了多款性能优越的NAND FLASH(闪存)和SDR/DDR(单/双数率同步动态存储器)等存储芯片产品。这些产品不仅在韩国市场上获得了良好的口碑,同时也开始逐步进入国际市场。

随着技术的不断进步和市场的不断扩大,FIDELIX逐渐在韩国存储芯片领域崭露头角。公司不断投入研发资金,加强技术创新,努力提升产品的性能和品质。同时,FIDELIX也积极拓展销售渠道,与多家国内外知名厂商建立了合作关系,为公司的持续发展奠定了坚实的基础。

DIALIGHT公司的发展小趣事

DIALIGHT公司的故事始于1938年的纽约布鲁克林,当时该公司专注于为飞机生产仪表板灯。随着技术的不断进步和市场的变化,公司在1971年,即LED推出仅一年后,推出了他们的第一个LED产品。这一举措标志着DIALIGHT正式从传统的飞机仪表板灯制造转向LED照明技术的研发和应用。从此,DIALIGHT彻底改变了LED的用途,将其广泛应用于世界各地的交通控制、指示灯、结构塔和工业场所,为全球提供了优质的照明解决方案。

问答坊 | AI 解惑

自恢复保险使用心得

1,现在市场上有除了瑞侃之外的JK系列,可以完全互换; 2,我接触的主要有两种电压规格60V和250V,交直流均可以用,指的是击穿电压; 3,额定电流是在一定的条件下给出的,如果要求工作在较宽的温度范围,应该留有一定的裕量,一般可以取1.5-2 ...…

查看全部问答>

uclinux jedce_probe探测ID,变成flash的内容

Flash:两片SST39VF3201 ,一片挂CS0:地址0x80000000 一片挂CS1:地址0x81000000 使用jecdec探测 static struct map_info lpc24xx_map[2] = { {         .name =                \ ...…

查看全部问答>

运行CETK时出现的问题 0x00000000”指令指引的“0x00000000”内存,该内存不能为“read”!

如题!用CETK调试驱动时,cetest.exe出现上述问题 。看了网上对这类问题的说法五花八门的,也不知道问题出在哪里,设备端clientSide.exe运行是正常的!…

查看全部问答>

猎头职位:多媒体开发~上海多个职位

汽车音响高级软件工程师 职位描述: 汽车音响嵌入式软件开发 职位要求: -电子工程或相关专业本科以上学历; -2-3年以上汽车电子相关工作经验; -熟悉嵌入式,单片机开发,有独立的开发能力; -熟悉汇编语言及C语言; -熟悉汽车总线控制 ...…

查看全部问答>

无法加载流驱动

各位高手: 本人新手,所用设备是pocket pc 2003,在PB5.0下写完了一个测试驱动flashtest.dll,编辑注册表,注册表中Dll,Prefix,Order和Index字段都设置好了,DLL文件放在\\windows目录下,重启后,pocket pc 2003中注册表字段都在,但device.exe ...…

查看全部问答>

如何开发内核流驱动程序?

最近一直在看DDK中的MSVAD,想开发虚拟声卡驱动。都说看DDK,但是看的不知所云,一些概念不清楚,如mixer,MixerMute,Adapter,Topology,Miniport,还有端口等等一些概念,请问这些东西是关于什么的?哪里有相关的资料看? 小弟跪求帮助!!!! ...…

查看全部问答>

关于ADC0809与AT89S51接口程序中的一条指令的疑问,请大家帮帮忙~

   ……       MOV DPTR  #0FEF8H   ;指向ADC0809首地址          MOVX @DPTR , A      ;启动A/D转换         ...…

查看全部问答>

请问专家,每个厂家的MAC地址是怎么规定的?

最近研究的一个课题涉及到MAC地址。世界上的每个网卡都有一个独有的MAC地址,我想知道是不是有什么组织向生产网卡的这些厂家分配网卡地址?他们分配的原则是什么?有没有像IP地址分类一样,MAC地址也有一些分类呢?…

查看全部问答>

在用TI的OMAP35xx系列产品,性能不够怎么办

之前遇到很多朋友在用TI的OMAP3530及OMAP3515等Sitara相关产品。他们开发了很多优秀的产品,如:KTV点播产品,大型游戏机等。最近有朋友问我,“David,我们使用OMAP3530开发的产品,觉得非常不错,可是我们现在有一个担心,要是我们将来想在产品上 ...…

查看全部问答>