Cx51程序设计的堆栈空间计算方法

发布者:EnigmaticCharm最新更新时间:2010-12-18 关键字:Cx51  堆栈  RAM 手机看文章 扫描二维码
随时随地手机看文章

  引言

  用C语言进行MCS51系列单片机程序设计是单片机开发和应用的必然趋势。Keil公司的C51编译器支持经典8051和8051派生产品的版本,通称为Cx51。应该说,Cx51是C语言在MCS51单片机上的扩展,既有C语言的共性,又有它自己的特点。本文介绍的是Cx51程序设计时堆栈的计算方法。

  1堆栈的溢出问题

  MCS51系列单片机将堆栈设置在片内RAM中,由于片内RAM资源有限,堆栈区的范围也是有限的。堆栈区留得太大,会减少其他数据的存放空间,留得太少则很容易溢出。所谓堆栈溢出,是指在堆栈区已经满了的时候还要进行新的压栈操作,这时只好将压栈的内容存放到非堆栈区的特殊功能寄存器(SFR)中或者堆栈外的数据区中。特殊功能寄存器的内容影响系统的状态,数据区的内容又很容易被程序修改,这样一来,之后进行出栈操作(如子程序返回)时内容已变样,程序也就乱套了。因此,堆栈区必须留够,宁可大一些。要在Cx51程序设计中防止堆栈的溢出,要解决两个问题:第一,精确计算系统分配给用户的堆栈大小,假设是M;第二,精确计算用户需要堆栈的大小,假设是N。要求M≥N,下面分别分析这两个问题。

  2计算系统

  分配给用户的堆栈大小Cx51程序设计中,因为动态局部变量是长驻内存中的,实际上相当于局部静态变量,即使在函数调用结束时也不释放空间(这一点不同于标准C语言)。Cx51编译器按照用户的设置,将所有的变量存放在片内和片外的RAM中。片内变量分配好空间后,将剩下的空间全部作为堆栈空间,这个空间是最大可能的堆栈空间。当然,因为Cx51是一种可以访问寄存器的C语言(特殊功能寄存器),因此可在程序中访问SP,将堆栈空间设置得小一点。不过,一般没有人这么做。本文只是讨论放在片内RAM的变量。我们把变量分为两种情况:

  ① 用作函数的参数和函数返回值的局部变量。这种变量尽量在寄存器组中存放。为了讨论方便,假设统一用寄存器组0,具体的地址为0x00~0x07。最多可以传递3个参数,如果参数的个数比较多,就将多余的参数放到内存(0x08以后的地址)中存放。这里,假设每个函数的参数都不大于3个。

  ② 我们在程序中定义的全局变量,以及不是用作函数的参数和函数返回值的局部变量。以上两种变量在内存中0x08地址以后存放,存放完毕后将堆栈指针SP指向分配了变量的片内RAM的最后一个字节。因为MCS51单片机的堆栈是一种满递增堆栈且堆栈的宽度为8位,所以在需要压栈操作时将堆栈指针先加1,后入栈有效内容。有了以上规则,就可以精确地计算出系统分配给用户的堆栈空间。以求两个数的最大公约数和最小公倍数的函数为例,代码如下:

程序

  这段程序中资源的分配情况如下:一个全变量M(无符号字符型)存放最大公约数;主函数中定义一个局部变量n(无符号字符型)存放最小公倍数;求最大公约数的函数unsigned char max(unsigned char a, unsigned char b),有两个参数a和b;求最小公倍数的函数unsigned char min(unsigned char a, unsigned char b),有两个参数a和b,并且定义了一个变量k存放函数的返回值。可以由此计算出系统分配给变量的空间。函数的参数和返回值在工作寄存器组中存放,所以不占用0x08地址以后的空间。系统只给变量M和变量n分配存储空间,这两个变量占两个字节(地址为0x08和0x09),则堆栈指针SP应该指向0x09。Cx51系统编译后生成代码的系统资源占用情况如下:全局变量M的地址为0x08,n的地址为0x09,SP的值为0x09。这与我们的计算结果相符。

  3计算用户需要堆栈的大小

  堆栈区到底留多大才算足够呢? Cx51程序设计中,用户需要堆栈的大小可以从普通子函数和中断子程序的嵌套层数来计算。普通子函数的调用比较简单,每次调用时就是将函数的返回地址保存在堆栈中,这个地址占两个字节。函数嵌套调用时,从最内层的子函数算起,总的堆栈需求字节数为嵌套的层数乘以2。中断子程序的堆栈需求分为两种情况:

  ① 中断子程序使用中断发生前的寄存器组。在中断发生时,保存中断子程序的返回地址需要2个字节。中断发生后,在中断子程序中系统会自动进行如下操作:将ACC、B、DPH、DPL、PSW、R0~R7共13个寄存器压栈。加上中断返回地址,中断的堆栈需求为15个字节。

  ② 中断子程序使用自己专用的寄存器组。这种情况下不需要保存R0~R7的内容,可以减少堆栈需求,其他的内容仍需要压栈保护。中断发生时,保存中断子程序的返回地址需要2个字节。中断发生后,在中断子程序中系统会自动进行如下操作:将ACC、B、DPH、DPL、PSW共5个寄存器压栈。加上、中断返回地址,这种堆栈的需求为7个字节。但是这种情况应该注意:如果中断子程序中调用子函数,且函数需要参数和返回值,则被调用的子函数和中断子程序要使用相同的寄存器组,否则会出现不可预料的后果。以一个温度测试系统为例。系统采用8051作为处理器,温度信号在A/D转换结束后通过外部中断0提醒单片机接收处理。定时中断0作为监控程序,中断周期为20 ms。温度信号可以自动测量(每秒一次)或者手动测量(按测量键后测量),这两种测量方法可以通过控制键切换。中断子程序和普通子函数的嵌套情况为:在定时中断程序中调用显示子程序,外部中断0内部没有函数调用。部分程序如下:

程序

  接下来分析这段程序的最大堆栈需求。假设定时器0中断时,调用了显示函数void leddisp(unsigned char *pt),在调用显示函数时A/D转换结束发生了外部中断0的中断。这时应该是程序对堆栈的最大需求,堆栈的大小是:定时器0(15字节)+显示函数(2字节)+外部中断0(7字节)=24字节。

  结语

  通过精确的计算编译系统分配给用户的堆栈空间和用户自己最大的堆栈需求,不仅能从根本上解决堆栈溢出的问题,还可以很好地安排单片机比较紧张的资源。此外,通过在片内存储器存放适量局部变量,还可以有效地提高软件的执行速度。

关键字:Cx51  堆栈  RAM 引用地址:Cx51程序设计的堆栈空间计算方法

上一篇:基于C8051F310和CS5460A的电压电流表设计
下一篇:基于AFS600的太阳能热水器通用控制器设计

推荐阅读最新更新时间:2024-03-16 12:31

stm32的内存分布
一、MDK下的概念 1)Code:代码段,存放程序的代码部分。 2)RO-data:只读数据段,存放定义的常量。 3)RW-data:读写数据段,存放初始化为非0值的全局变量和静态变量。 4)ZI-data:零数据段,存放未初始化及初始化为0的全局变量和静态变量。 所以在stm32中:flash的容量是前三项相加(RW-data的初始值也要存在flash里),RAM的容量是后两项相加等。 static变量(函数内静态局部变量和函数外静态全局变量)初始化的、未初始化的分别在RW-data、ZI-data; 全局变量初始化的、未初始化的分别在RW-data、ZI-data; 局部变量(函数内)在栈段,动态分配的空间在堆中,
[单片机]
stm32的<font color='red'>内存</font>分布
在IAR下如何查看工程的堆栈(STM32)
在IAR下编程,难免少不了要跟堆栈打交道,但是打交道之前也要知道堆栈的大小,以及堆栈的位置在哪里? Options- Linker- Overridde default- Edit查看芯片的堆栈大小 这里可以看到我们使用的堆栈的大小都是0x800(2K)大小。如果想修改堆栈的大小,可以在这里修改,然后保存到工程目录下即可。 那如何查看堆栈在SRAM中的位置呢? 首先设置Options- Linker- List,将Generatee linkeer map file勾选上。 编译一下,在工程目录的output文件夹下就出现了.map的文件。 在文件中我们可以看到一下分配信息。 可以看到堆栈都在 P2
[单片机]
在IAR下如何查看工程的<font color='red'>堆栈</font>(STM32)
金士顿宣布其两款DDR5内存已通过英特尔认证
据外媒techpowerup报道,金士顿于10月6日宣布其两款DDR5 UDIMMS台式机内存条已通过英特尔平台认证( Intel Platform Validation),这是一项重要的里程碑,代表着其产品将与第12代酷睿处理器兼容。 金士顿将推出一系列高性能DDR5内存,覆盖低容量至高容量,提供多种外形。官方表示,在开发出最新DDR5内存之后,已经向主板制造商和合作伙伴发出了10000多个DDR5 UDIMM内存条样品,以便尽早为产品的正式发布奠定基础。 根据研究机构TrendForce的最新数据,金士顿已经成为全球顶级的DRAM内存模组供应商,收入排名第一。金士顿高管指出,公司已经为DDR5内存产品进行了大量
[半导体设计/制造]
金士顿宣布其两款DDR5<font color='red'>内存</font>已通过英特尔认证
解决STM32 不能读RAM 下载的问题
制作了两个ARM-OB下载器,原以为会很快搞定,谁知道,在最后一步出现了如下的问题: ERROR: RAM check failed @ address 0x20000000. - ERROR: Write: 0xE7FEBE00 E07CE062 - ERROR: Read: 0x0000000 000000000 - ERROR: (0 bytes of RAM have been checked successfully) - ERROR: Failed to read back target memory 我感觉好奇怪啊,三个月前我制作ARM-OB下载器的时候还没有这个问题出现,为什么呢? 百度该问
[单片机]
在ARM开发环境下C语言的设置堆栈指针和清理BSS段的作用概述
以前稍微写过操作系统上的C程序,感受不出来:BSS段,堆栈的意义。到了在单片机上写程序也没有考虑这些问题。但是到了ARM上环境似乎没有那么简单了,C的环境要自己来创建,不然就不能用。这也深刻的感受到了C语言中原来难以理解的概念。 裸机建立C语言环境-设置堆栈指针 这个是使用C语言的首要条件,不过这个就是指定一个sp指针就可以了,很简单的。ldr sp, =4096。 裸机建立C语言环境-清理BSS段 如果C语言中用到的全局变量或者静态变量,这个编译的时候是把它们放到了BSS段,这个段在内存中。怎么建成的?手动写一个链接脚本,添加__bss_start __bss_end变量来表示BSS段的开始和结束。如下: SECTION
[单片机]
在ARM开发环境下C语言的设置<font color='red'>堆栈</font>指针和清理BSS段的作用概述
红魔7 Pro现身跑分平台 搭载16GB RAM
努比亚红魔7系列手机将于2月16日在中国市场首次亮相,系列将包括红魔7和红魔7 Pro。到目前为止,我们知道前者的型号是NX6791,而后者的型号是NX709J。 之前,标准版本通过Geekbench认证取得了令人印象深刻的成绩,现在,型号为NX709J的Pro版本也出现在了同样的认证平台上。 跑分显示,型号为NX709J的红魔7 Pro的单核得分为1264分,多核得分为3744分。 这款设备搭载Android 12,内存为16GB。就像标准机型一样,Pro机型也采用骁龙8芯片。 除此之外,该设备还出现在Wi-Fi联盟认证上,确认支持2.4GHz和5.0GHz Wi-Fi。
[手机便携]
红魔7 Pro现身跑分平台 搭载16GB <font color='red'>RAM</font>
VB环境下对双端口RAM物理读写的实现
    摘要: 介绍应用双端口RAM芯片设计的智能型高速并行通讯卡。针对VB语言环境下,用DLL函数链接方式,对采用内存直接映象技术的双端口RAM进行读写,实现主、分机之间数据高速并行传输。本技术已经成功应用在汽车给合检测系统中,对在其它集散型控制系统中进行高速数据传输同样有益。     关键词: 双端口RAM 内存直接映象 高速并行传输 DLL动态链接 在集散型控制系统中,一般将计算机或工控机用于终端图文显示,数据采集处理以及人机对话接口等方面。计算机或工控机与外部设备需要建立数据传输的通讯联系。但大量数据传输,靠通常的串行通讯方式进行,必须占用CPU大量的时间进行通讯。一些控制系统往往因为传输速率慢而无法
[缓冲存储]
黑鲨新机现身谷歌Play:FHD+屏幕与12GB内存,可望为黑鲨4 Pro
IT之家2月7日消息 上周,在谷歌 Play 上出现了一台代号为 “ kaiser”、型号为 KSR-A0 的黑鲨智能手机,该手机搭载 FullHD + 屏幕和 8GB 内存,据预计为黑鲨 4。   而现在,另一款新的黑鲨手机也出现在了谷歌 Play 上,型号名称为 PRS-A0,代号为 “ penrose”。   从上架信息可以看出,该手机搭载 Android 11 系统、12GB 内存和 FullHD +(1080x2400)屏幕。处理器是 SM8250,即高通骁龙 865,但这一信息可能也是错的,上一款的处理器信息显示为高通骁龙 835,但实际上两款新机都有望搭载高通骁龙 888 处理器。   谷歌 Play 中还
[手机便携]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved