ICCAVR使用两个堆栈:一个用于子程序调用和中断操作的硬件堆栈,一个用于传递参数、临时变量和局部变量的软件堆栈。硬件堆栈是从数据内存的顶部开始分配的,在硬件堆栈下面再分配一定数量的字节作为软件堆栈。硬件堆栈和软件堆栈均为向下生长型的堆栈。
如果函数的调用层次太深,有可能会发生硬件堆栈溢出到软件堆栈中,改变了软件堆栈中数据的内容。同样,当定义了太多的局部变量或一个局部集合变量太多也有可能出现软件堆栈溢出到动态分配的数据区,两个堆栈都有可能溢出,如果堆栈溢出,会引起不可预测的错误。可以使用堆栈检查函数检测两个堆栈是否溢出。在Target的页面中有一个Return Stack Size选项,用于指定硬件堆栈的大小,通常如果子程序调用嵌套不深(不超过4层),那么使用默认的16B就足够了,如果使用了浮点函数,则至少应设定为30B。在一般情况下,除了层次很深的递归调用及使用了%f格式说明符外,设定为40B就足够了。
启动代码在硬件堆栈和软件堆栈的最低字节分别写进一个代码(oxaa),把这个代码称为警戒线。如果硬件堆栈和软件堆栈溢出过,则警戒线字节的代码(oxaa)就会改变,堆栈检查函数就是通过检查这两个堆栈的最低字节的代码是否被改变来判断两个堆栈是否溢出。通过调用_StackCheck(void)函数来检查堆栈溢出,如果警戒线字节中的代码仍然保持正确的值,那么函数检查通过,没有溢出。如果堆栈溢出,那么警戒线字节将可能被破坏,_StackCheck(void)函数检查到警戒线判断字节中的代码被改变,就判断相应的堆栈溢出(当程序堆栈溢出,程序可能运行不正常或偶然崩溃),该函数再调用函数StackOverflowed(charc),如果参数是1,那么硬件堆栈有过溢出;如果参数是0,那么软件堆栈曾经溢出。
在使用堆栈检查函数时应注意以下几点:
(1)在使用堆栈检查函数时,必须先用#include“macros.h”预处理。(www.xing528.com)
(2)如果使用自己的启动文件,在ICCAW6.20以后的版本中,如果使用的启动文件中没有警戒线的内容,ICCAVR也会自动添加警戒线。而在ICCAVR6.20以前的版本中,必须自己添加该部分内容,否则生成的代码中堆栈分配将不带警戒线。
(3)如果使用动态内存分配,必须跳过警戒线字节_bss_end来分配堆(增加一个字节)。
(4)当_StackCheck(void)函数检测到警戒线字节被改变,则会调用一个默认的Stack-Overflowed(char c)函数来跳转到程序存储器0的位置(复位向量地址)。可以指定或重新编写一个新的函数来代替它,例如可以用新函数来指示是哪个堆栈溢出等,但这个函数也不可能执行太多的功能或让程序恢复到正常状态。因为堆栈溢出后,会更改掉一些有用的数据,引起不可预测的错误,甚至使程序死机。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。