×
嵌入式 > 嵌入式开发 > 详情

慎用51单片机中的RET指令

发布时间:2020-08-20 发布时间:
|
题目:已知有四个按键依次连接单片机中的P3口的0到3的IO口,有四个LED灯连接P1的0到3 IO口,写一程序,满足以下条件:当按下按一个按键,对应的LED会发亮,比如

按下P3.0的按键,连接P1.0的LED就发亮。y

以下是我同学编写的程序:

org 0000h
mov P1,#0ffh
loop:
jnb P3.0,led1;*
jnb P3.1,led2;*
jnb P3.2,led3;*
jnb P3.3,led4;*
ljmp loop
led1:
clr P1.0
ret
led2:
clr P1.1
ret
led3:
clr P1.2
ret
led4:
clr P1.3
ret
end

程序的意图是,制造一个死循环,不断检查按键是否按下,如果按下,就令对应的灯亮。程序经过测试,能够满足题目的要求。

但是,问题出现在上面带*号的那一部分代码,程序意图是想要当P3的某个位为0的时候,就调用LED灯的子程序,执行CLR P1.0语句,再返回到原来程序调用子程序的地方继续执行代码。

我对的子程序的理解是:在一个地方启动一段代码,当这段代码运行完毕之后,就返回到原来的地方继续运行剩下的代码。

那么CPU单片机是如何返回原来的地址的呢?

首先,当程序执行到A处进入子程序时,将A的下一个条指令(即PC+2所指的地方)压入栈中,即将栈指针SP+1,PCL进栈,SP再加1,PCH进栈。

然后,把PC的值改为子程序代码的入口。

子程序执行完毕之后,从栈中弹出原来的PC值,赋值给当前的PC寄存器。


最后,程序返回到原来调用子程序的地方的下一条指令继续运行。

(详细步骤请查看RET和ACALL,LCALL指令)

上面的代码很明显想调用一个子程序,但是51单片机中,只有ACALL和LCALL指令会在跳转前讲PC+2值压栈,其他跳转指令都不会。

代码中使用了JNB作为跳转指令,所以并没有压栈,但是当跳转之后遇到RET,还是一如既往地弹栈,这样,只有出,没有进,会导致堆栈不平衡。

但为什么这个程序依然有效呢?

这个因为SP初始指针指向了一个空白的单元(全是0),所以,当遇到RET后,把PC寄存器给初始化,程序由头开始重新执行,阴差阳错地满足的题目的要求。

所以RET指令必须和ACALL和LCALL配套使用,才能组成为真正意义上的子程序



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
盘点几种主流嵌入式架构的代码压缩技术