本帖最后由 dark_sky 于 2016-11-23 19:33 编辑
手头有个STC90C52的程序,keil C写的,修订版的上电几分钟就重启,没有用看门狗,排除了硬件掉电、数组或指针的越界操作,考虑是否堆栈溢出,手动更改了START51.A文件将STACK放在了DATA区,并将STACK长度定义为30H(子程序调用最大8层,开串口中断,串口中断调用一个非重入处理子函数,这个结构貌似不合理,但因为某些原因,只能这么搞,汗~~~),结果重启没有那么频繁了,大概半小时一次,这下要考虑重新排查数组指针越界了。
这里面有个问题,子程序调用压栈操作2字节PC指针,中断现场要再压栈7字节(这个芯片的DPTR有两组,所以多2字节)寄存器和2字节PC,算起来最多是:8*2(普通子程序)+7(反汇编中断的直接PUSH和POP指令,状态寄存器)+2(中断现场)+2(中断中的子程序)=27字节=0x1B字节,所以30H字节的STACK应该足够用。
好吧~~~这些都不是重点,重点是:STC的手册和Keil的手册上对51的STACk都是只要在RAM中就行,STC的手册中更是明确提出更建议在80H以后,也就是128~255地址的RAM;但在另外的文章里指出,系统的STACK应该是在7FH以下,因为POP和PUSH操作只能针对直接寻址单元操作,也就是低128字节,而高128字节则因为对应的直接寻址是SFR而不能用堆栈操作。而且貌似在多数的C51手册里 对0x00~0x7F地址RAM的说明里都有出现“大多数程序会把堆栈放在这以空间”的说法。那么问题来了~~~到底谁说的对?
还有——系统在子程序调用(ACALL、LCALL)和中断处理时的压栈和出栈操作我们不用去写PUSH和POP指令的(中断有部分需要),但是它是不是也和PUSH和POP完全一样,会不会用到不是直接寻址的单元,比如高128字节,只是它是内部操作,用别的办法代替了@Ri一类的间接寻址?
为此自己试验了下,在不更改STACK长度和位置时,重新编译下载,大约每2分钟重启,M51文件中看到的STACK位置是8EH,长度是1,当然后面都是GAP了,反汇编窗口看到的中断程序入口PUSH了7个字节,出口POP了7个字节。然后我更改STACK长度和位置定为DATA后,每30分钟随机左右重启,而且之前的工作中也遇到过编译后的程序占用data超过128字节时类似的问题,采用large模式后就正常了。所以一厢情愿的相信了STACK应该在低128字节,不知道对不对。
呵呵,所以又去请教了原厂的技术支持,得到的答案是用C的时候不要去管汇编,不要去动STACK或SP,keil会把一切搞的妥妥的.当然去除个人意见和对这位高手水准的猜测,我愿意相信他说的是对的,但仍然不知道到底STACK在高128字节时会不会出问题,好想弄清楚...
所以……高手们来a,谢谢不吝赐教^_^ ^_^ ^_^
~好吧,已经解决了,AT89C52 E文原版P6左上角有说明,堆栈本身实际就是间接寻址(用的SP寄存器),所以对于C52以后的片子来说0~256整个RAM空间都是可做为堆栈的。但PUSH和POP的源数据必须是直接寻址(这个指令里有说明)。至于别的,已经回复给了各位,等待
论坛审核过了应该就可以看到了。
而如果说堆栈要放到80H以后是为了安全的话,个人觉得不如说是为了将80H前的留给更能发挥其作用的数据用,不过如果放到更低的地址,比如30H或者07H确实有可能因为别的数据可能是被分配在了堆栈后面的地址,比如将变量全局变量XX分配在了7DH,这样如果程序运行是的堆栈操作很多,可能最后导致栈顶SP指向了7DH,而在之后的堆栈操作比如函数调用时压栈PC将会改变XX的值,还可能因为改变XX值的操作而导致出栈时的PC值已经不是原先的值了(这是已经变为了XX改变后的值),这些都是不符合期望的。
问题已解决,是一个忘记的数组越界操作了。之前说的文章里有提到堆栈应操作应该是00~7FH是本人理解有误,其本意应该是在说PUSH和POP的操作数也就是源数据是00~7FH而不是堆栈地址应该在00~7FH。手册中所提到的多数程序将堆栈放在00~7FH也确有其事,但不是一定要放在这里。
兜纳K1
一周热门 更多>