51单片机的仿真栈(模拟栈/可重入栈)

发布者:technology1最新更新时间:2018-10-10 来源: eefocus关键字:51单片机  仿真栈  模拟栈  可重入栈 手机看文章 扫描二维码
随时随地手机看文章

首先来看,51的系统栈(又叫系统栈,或者硬件栈),就是SP所指向的栈,他是一个满增栈(注释1),位于片内RAM的128 bytes之中,上电之后系统堆栈指针SP的初值等于多少呢?这个要从51的启动文件来分析,启动文件中有这样的汇编代码:


?STACK SEGMENT IDATA ;定义一个片内数据段,段名:?STACK


RSEG ?STACK ;选择之前定义过的一个可重定位的段?STACK,下面的汇编语句将会被放置到该段,直到遇到下一个段定位指令,例如CSEG/RSEG。


DS 1 ;预留存储区命令。声明先占用一个字节的空间,在编译时,这个预留的空间不会被其他变量所使用。在这里的意义是,给硬件栈分配1个byte(实际这样是有问题的,应该为硬件栈预留更多空间)


还有:


MOV SP,#?STACK-1


由上可见,SP被初始化为#?STACK-1,在#?STACK地址处,DS指令预留了N个字节的空间,这些空间就是硬件栈的空间


但启动文件的代码中,DS 1相当于只给硬件栈预留了1个字节,这实际上会出问题,原因如下:片内RAM中会有多个数据段,只要使用XX SEGMENT IDATA指令即可在片内RAM中声明一个数据段XX,如果整个工程程序中,声明了多个数据段,?STACK数据段就只是片内RAM中众多数据段中的一个,如果只给?STACK段预留1个字节,而?STACK数据段后面又有别的数据段,那么我们的硬件栈就只有1个字节了,一旦发生中断,CPU寄存器自动入栈立即导致栈溢出,溢出后踩了别的变量的内存,程序基本崩溃;对于这个问题,keil是这样处理的:keil在链接阶段总是把?STACK数据段链接为片内RAM中的最后一个数据段,即使我们只给他预留了1个字节,那也不要紧,反正该段后面没有别的变量占用,只要SP别超出0X7F(片内RAM地址的上限)就行了。通过观察.m51(map文件)我们发现,keil确实是把?STACK数据段放到了片内RAM的最后,下面是某个51工程生成的map文件摘抄:


* * * * * * * D A T A M E M O R Y * * * * * * *


REG 0000H 0008H ABSOLUTE "REG BANK 0"


DATA 0008H 0002H UNIT ?C?LIB_DATA


IDATA 000AH 000DH UNIT ?ID?UCOS_II


0017H 0009H *** GAP ***


BIT 0020H.0 0000H.1 UNIT ?BI?SERIAL


0020H.1 0000H.7 *** GAP ***


IDATA 0021H 0041H UNIT ?STACK ; 作者注:就是这一行!


* * * * * * * X D A T A M E M O R Y * * * * * * *


XDATA 0000H 080EH UNIT ?XD?SERIAL


XDATA 080EH 0804H UNIT ?XD?MAIN


XDATA 1012H 0490H UNIT ?XD?UCOS_II


XDATA 14A2H 005CH UNIT _XDATA_GROUP_


为避免系统栈不够用,一个比较稳妥的办法就是,用汇编指令DS给?STACK数据段预留更多的空间,上面这个51工程中在另一个汇编文件中又给?STACK数据留出了40H个字节,这样总共就有41H个字节了。这样做的好处是可以在编译链接阶段即可排查堆栈错误,举个例子: 假设片内RAM中的数据段有很多,以至于,除了?STACK数据段之外,片内RAM只剩2个字节了,而?STACK数据段我们只默认采用了启动文件中的配置预留一个字节,这样编译没有任何问题,keil给编译通过了,但是运行过程中系统栈只有2个字节,肯定是分分钟就发生栈溢出,然后崩溃;假设片内RAM中的数据段有很多,以至于,除了?STACK数据段之外,片内RAM只剩2个字节了,而如果我们给?STACK数据段用DS指令分配40H个字节,这样keil在编译时就会发现51的片内RAM不足而报错,无法编译,从而在编译链接阶段帮助我们发现堆栈问题。


继续上面的问题,SP复位后的初值是多少,SP复位后等于0X07,但是立即就被启动文件通过语句MOV SP,#?STACK-1给改掉了,所以在进入main函数时SP的值是启动文件修改后的值,也即#?STACK-1(注,很好理解,这里-1是满增栈的特性),那么#?STACK的值又是多少呢?看上面的汇编语句?STACK SEGMENT IDATA,这一句声明?STACK段为一个可重定位的段,也就是说,?STACK段的首地址(#?STACK)在编译器进行程序链接时才能确定下来,也就是说,#?STACK的值是在链接时由编译器自动分配的,编译阶段不分配。仍然以上面摘抄的这段map文件为例,我们发现,?STACK段的起始地址是0021H,也就是说,#?STACK就等于21H。


仿真栈是keil为51生成可重入函数时用的(通过给函数使用关键词 REENTRANT限定,可使该函数具备可重入特性),对于STM32来说,默认生成的函数(不含全局变量和静态局部变量的函数)就是可重入的,而keil为51生成的函数,即使这个函数不含全局变量和静态局部变量,默认情况下keil也不会把这个函数汇编成可重入的,我认为keil主要是考虑到51的片内RAM匮乏,在不外接RAM的情况下,函数如果被编译为可重入的,可重入函数的执行需要占用一定的栈空间(尤其是由可重入函数嵌套调用产生的长的调用链,所需的栈更多)。


可重入函数在执行过程中是需要使用栈的,那么51的可重入函数使用的栈在哪呢?是SP指向的那个系统栈吗?答案是:不是。下面是解释:


当我们给51外扩了大的片外RAM时,就不用担心RAM不够的问题了,但是还有一个问题,系统栈指针SP只能寻址0~7FH共128字节的空间,可重入函数肯定不允许被编译成使用系统栈,否则,就算外扩了RAM,这个外扩RAM又无法供系统栈来使用,外扩RAM就没有意义了,所以keil为51打造了一个仿真栈的概念,keil在启动文件中声明了一个1或2字节的变量作为栈指针,这个栈指针的名字和大小根据编译模式的不同而不同,以大编译模式(注释2)为例,大编译模式下,启动文件中的XBPSTACK常量需要程序员手动设置为1,这样启动文件中使用到的条件编译,将会引用到一个2字节的仿真栈指针?C_XBP,由于keil把仿真栈作为满减栈,所以这个仿真栈指针?C_XBP被初始化为片外RAM地址的最大值加1,若我们外接了一个64K的片外RAM,该RAM的最大地址是0XFFFF,那么栈指针?C_XBP被初始化为0XFFFF 1=溢出为0x0000。再举一个小编译模式的例子,小编译模式是用来给没有外扩RAM的51用的,这样51只能使用片内0~127共128字节的RAM(这128RAN中还有一部分是Rn等,留给程序可用的RAM就更少了),在小编译模式下,keil给51生成的仿真栈指针名叫?C_IBP,同时需要程序员手动把IBPSTACK常量设置为1,指针?C_IBP的初值被初始化为可用RAM的最大地址(127)加1,也即0x7f 1。关于小编译模式small、压缩编译模式compact、大编译模式large在堆栈处理上方面的不同,可参考这篇文章点击打开链接,如果链接挂了,可自行搜索:《Keil模式设置和编程事项》。


注释1:满增栈,满指的是SP总是指向最后一个入栈的字节的地址,增指的是每入栈一次,SP变大。相应的,还有空增栈、空减栈、满减栈,空指的是SP总是指向栈中下一个空闲位置的地址。


注释2:如何选择大编译模式:以keil5为例,依次选择->魔术棒->Target选项卡,Memory Model选择Large:var...,Code Rom Size选择Large....


附:举一个不可重入函数使用中可能发生的陷阱,假设有分别有如下两个函数,第一个可重入,第二个不可重入


int add5_re(char a1,char a2,char a3,char a4,char a5) REENTRANT


{


int sum;


sum=a1 a2 a3 a4 a5;


return sum;


}


int add5(char a1,char a2,char a3,char a4,char a5)


{


int sum;


sum=a1 a2 a3 a4 a5;


return sum;


}


这两个函数的形参以及局部变量分配等信息我们查阅.m51文件,分别如下(分号后面的注释是博主自己加上的):


[plain] view plain copy------- PROC _?ADD5_RE


x:0002H SYMBOL a1 ;注意,地址标号前为小x,指a1倍分配到了仿真栈中


x:0003H SYMBOL a2


x:0004H SYMBOL a3


x:0005H SYMBOL a4


x:0006H SYMBOL a5


------- DO


x:0000H SYMBOL sum


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


------- PROC _ADD5


D:0007H SYMBOL a1 ;R7


D:0005H SYMBOL a2 ;R5


D:0003H SYMBOL a3 ;R3


X:14ABH SYMBOL a4 ;注意地址标号前为大X,指外部RAM


X:14ACH SYMBOL a5


------- DO


D:0006H SYMBOL sum ;R6


我们发现,add5中的形参和局部变量a1/a2/a3/sum分到了Rn中,a4/A5分到了外部RAM xdata的绝对地址处,如果我们在main的调用链中和中断函数中都调用了add5这个函数,就会发生错误,假设恰好在main的调用链中执行add5时发生了中断,切换到中断函数中去执行add5,那么main调用链中的a1/a2/a3/sum因为被分到了Rn中,进入中断会切换register BANK,使得main调用链中的a1/a2/a3/sum没有被破坏,得以幸免,但是a4/a5因为被分配到了绝对地址中,在中断执行完add5以后,main链条中的add5的a4/a5肯定会被破坏!!


对于可重入的add5_re函数,即使main调用链和中断同时调用它也不会出现上述被破坏的情形,因为add5_re的形参和局部变量全部都被定义到了仿真栈中(见上述代码注释),main调用链中使用add5_re函数会申请栈空间,中断时add5_re又会申请新的栈空间。


还要注意的是,因为keil编译51程序时,使用了覆盖技术(不同函数的形参和局部变量可分时共享同一个绝对内存单元),这也有可能产生陷阱,假设这样一种情况:有一个函数func2( )的局部变量b在编译后被分配到了绝对xdata的地址14ABH处,和上文的add5的a4变量共享内存,这种情况下,即使 { func2( )仅在中断中被调用,main调用链中不调用func2( )}、且{ add5仅在main调用链中被调用,中断中不调用add5 },也会出问题,原因是显而易见的,如果在add5执行过程中发生中断,中断中使用过变量b之后,会破坏add5中的变量a4。究其原因在于,共享地址的编译方式生成的函数,只要分时调用就不会产生被破坏的情形,但是发生中断导致了分时机制被破坏,以至于产生了同时调用。


结论:中断中使用的函数,要么是可重入的,要么是该函数的局部变量全部是独享内存单元的。


关键字:51单片机  仿真栈  模拟栈  可重入栈 引用地址:51单片机的仿真栈(模拟栈/可重入栈)

上一篇:51单片机定时器使用经验总结
下一篇:51单片机存储器小结

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

51单片机综合学习系统之 步进电机控制篇
大家好,通过以前的学习,我们已经对51单片机综合学习系统的使用方法及学习方式有所了解与熟悉,学会了红外线遥控的基本知识,体会到了综合学习系统的易用性与易学性,这一期我们将一起学习步进电机控制的基本原理与使用方法。 先看一下我们将要使用的51单片机综合学习系统能完成哪些实验与产品开发工作:分别有流水灯,数码管显示,液晶显示,按键开关,蜂鸣器奏乐,继电器控制,IIC总线,SPI总线,PS/2实验,AD模数转换,光耦实验,串口通信,红外线遥控,无线遥控,温度传感,步进电机控制等等。 上图是我们将要使用的51单片机综合学习系统硬件平台,本期实验我们用到了综合系统主机、步进电机,综合系统其它功能模块原理与使用详见
[单片机]
<font color='red'>51单片机</font>综合学习系统之 步进电机控制篇
基于USB总线和89C51单片机的数据采集系统设计
在工业生产和科学技术研究过程的各行业中,常常要对各种数据进行采集,现在常用的采集方式是在PC机或工控机内安装数据采集卡,如A/D卡及RS-422卡、RS-485卡。采集卡不仅安装麻烦,易受机箱内环境的影响,而且由于受计算机插槽数量和地址、中断资源的限制,不可能挂接很多设备。而通用串行总线(Universal Serial Bus,简称USB)的出现能很好地解决以上这些冲突。我们利用89C51单片机设计了基于USB总线的数据采集设备,并可与MAX485结合起来实现数据的远程采集。 系统硬件设计 USB数据采集系统硬件模块主要由串行A/D转换器、89C51芯片、USB接口芯片和多路模拟开关等组成。硬件总体结构框图如图1所示。
[嵌入式]
STC51单片机实例之05数码管的各种显示方式
简介:本文主要是STC51单片机实例之05数码管的各种显示方式的程序代码,希望对你的学习有所帮助。
[单片机]
STC<font color='red'>51单片机</font>实例之05数码管的各种显示方式
51单片机基础之点阵LED8X8
原理: 他这个图是有问题的,大家不要被误导,例如我显示一个箭头,按照这个原理图来,那么中间那一根直线0xff,表示高电平有效,但是按照这个图来说P0是低电平有效,所以我觉得发光二极管反一下才符合代码的意思,希望不要被误导。 字模提取: 软件获取:链接:https://pan.baidu.com/s/1OFAR8a2CnTg6Nle2WDmIRA 提取码:1234 代码: #include reg51.h typedef unsigned int u16; //对系统默认数据类型进行重定义 typedef unsigned char u8; //定义74HC595控制管脚 sbit SR
[单片机]
<font color='red'>51单片机</font>基础之点阵LED8X8
51单片机系列之驱动蜂鸣器发声
名称:51单片机驱动蜂鸣器发声 平台:Keil 4, Ly-51S学习板 内容:模拟报警声,如闹钟 滴 滴 滴 滴 -----------------------------------------------------*/ #include reg52.h sbit SPK = P1^2; void delay_2us(unsigned char t) { while(--t); } void main() { while(1) { delay_2us(25); SPK = !SPK; } }
[单片机]
51单片机汇编——延时和点灯
前言 这篇文章主要是看视频学习51汇编,但是我的环境总是配置不好,出现了这个问题(keil2+vdmagdi.exe+Proteus),有懂的小伙伴可以帮助一下我 ^.^ 一、延时程序 1.1 延时程序的简介和分类 在单片机的控制应用中,常有延时的需要,CPU 过一段时间再去做某件事,称之为延迟。延时有两种方法,即软件延时和硬件延时 硬件延时是通过定时/计数器(中断程序)来实现的,这种方法不占用 CPU 的工作时间 软件延时一般采用循环程序,通过 CPU 执行一个具有固定延迟时间的循环体来实现的 1.2 软件延时 1、机器周期数 延时程序的延时时间主要与两个因素有关,一是所用晶振,二是延时程序中的循环次数,一旦晶振确定之后
[单片机]
基于51单片机的新型冰箱温度控制器系统
0引言 随着生活的改善,消费水平的提高。越来越多的普通居民开始使用冰箱。每年冰箱的市场额都在千万以上。随着中国电子行业的高速发展。一个个新型的企业开始计入抢夺市场的竞争中。使得供应生产商,在保证质量和顾客需要的前提下,纷纷消减陈本,制定不同的战略。目前各大冰箱控制器的生产厂家存在着更加残酷的竞争,只有把握住优质的技术,结合低廉的陈本才能为企业,迎来生存的机会。基于这种情形,我们认为现如今,在家电的低端市场,廉价实用性强的控制器为各大产品提供了巨大优势,这种优势是在竞争中无法忽略的。我们的产品追求廉价实用,节能环保,突出用户地位更加人性化,应用宽泛。 1 系统总体介绍 这是一个基于51单片机的电冰箱控制系统,通过51单片机控制
[单片机]
基于<font color='red'>51单片机</font>的新型冰箱温度控制器系统
基于51单片机的函数发生器设计
一.硬件方案 此函数信号发生器是基于单片机AT89C51设计而成的,能够产生频率范围在0Hz—535Hz的锯齿波、正弦波、三角波、矩形波四种波形,并且能够通过液晶屏1602显示各自的波形类型以及频率数值。 主要由51单片机+最小系统+DA0832模数转换模块+运放模块+LED指示灯+按键模块;如图: 二.设计功能 (1)LCD1602液晶显示波形种类和频率值(10-100HZ)。可产生正弦波、锯齿波、三角波、矩形波。 (2)按键设置波形种类和设定频率步进值。 (3)通过电位器器改变振幅(0V-3.5V稳定)。 (4)有四个指示灯分别指示发出的是哪种波形,方便明了。 三.设计原理图 (1)原理图主要采用AD软件进行设计,
[单片机]
基于<font color='red'>51单片机</font>的函数发生器设计
热门资源推荐
热门放大器推荐
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

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

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