二极管作左右跑马灯,当按下外部按键K0时,8个二极管全部闪烁5次后从K0按下之前的位置继续作跑马灯。
二、实验目的
掌握堆栈在中断程序中的作用
掌握让程序保护现场的方法
三、实验任务分析:
有了以前各个试验的经验,相信这个试验对我们来说,难度不是很大。我们唯一接触到的新的知识点是:让程序从返回中断之前的位置继续执行跑马灯,那么如何能够让程序在进入中断之前记住当时所处的位置,在执行中断之后,能够返回这个地方继续往下执行呢?
我们可以这样作:在进入中断之前,把该时刻的程序信息放到一个地方保存下来,在返回中断之前,再到这个地方把我们存放的程序信息取出来。这样不就可以从进入中断的位置开始重新执行程序了吗?那么,这个暂存数据的地方在哪里呢?
单片机给我们考虑的很周到,允许我们从内部RAM中指定一个空间专门来作这个工作,这个空间就是堆栈。并且单片机还专门给了我们一个8位的堆栈指针,让我们用它来开辟堆栈空间。(为什么是8位呢?因为内部RAM的地址空间是256字节,所以8位就足够拉。)
例如:假如我们给堆栈指针赋值:mov sp,#70h,就表示我们把内部数据RAM的地址为70h开始的单元设为堆栈啦。
那么,我们一般把内部数据RAM的那些地方作为堆栈呢?让我们来复习一下内部RAM的结构吧。
前面我们已经说过,内部RAM共有256字节,分为两组。还记得它们各自的功能吗?高128字节是特殊功能寄存器区,我们没有办法利用,那就打低128字节的主意吧。我们再来看看低128字节的RAM空间分配。
我们发现在低128字节中,工作寄存器区和位寻址区的地址已经分配好了,我们可以利用的只有30h~7fh的数据缓冲区了。所以我们的堆栈指针只能设在这个区域,从30h以后的范围为宜。在该程序中,我们把堆栈设在70h的位置。
好啦,知道堆栈设在哪里,下面我们就要考虑如何把程序运行的相关信息放入堆栈拉。那么,程序运行的相关信息在哪里呢?
由于在主程序中,我们让程序作左右跑马灯。还记得试验三吗,我们的左右跑马灯是通过把寄存器a中的数,通过进位标志CY(程序状态字PSW的最高位),进行左右环移来实现的。同时,由于寄存器a是单片机中最最常用的寄存器,我们在中断程序中也要用到它。为了避免中断程序改变寄存器a的值,所以我们在中断服务程序开始之前,把a的值放到堆栈中保存起来。同样我们也要把psw的值也保存起来。在返回主程序之前,再把它们取出来,这样就可以使得程序从进入中断之前的位置开始,继续作跑马灯。
把数据存入堆栈和从堆栈中取出,是通过堆栈操作指令完成的。
例如:如果想把a中的数据存入堆栈,就:push acc;如果想把a的内容从堆栈中取出,就:pop acc。(一般称之为:压入,弹出)。
还需要说明一点的是:堆栈中的数据是采用“后进先出”的结构方式处理的。就像我们摞盘子一样,最后摞进去的盘子,取得时候是最先取出的。所以我们压入数据后,再弹出的时候要特别注意顺序,后压入的要先弹出,不要弄错啦。
现在来看看这个试验的程序吧。
四、实验程序如下:
org 0000h
ljmp start
org 0013h
ljmp ext1
org 0020h
start: clr p1.5 ;避免蜂鸣器响
setb ea ;CPU开中断
setb ex1 ;允许外部中断1申请中断
setb it1 ;设置外部中断1跳变方式触发
mov sp,#70h ;设置堆栈入口
loop1: lcall light1 ;调用左右跑马灯子程序
ljmp loop1
;以下是中断服务程序
ext1: clr ea ;关闭CPU中断
push acc ;把寄存器a的内容压入堆栈
push psw ;把程序状态字压入堆栈
lcall keyreader ;调用键识别子程序
pass: pop psw ;恢复现场,注意顺序,要先弹出程序状态字
pop acc ;弹出寄存器a的内容,
setb ea ;CPU开中断
reti ;中断返回
light1: mov a,#0ffh ;light1是左右跑马灯子程序,大家可以参考试验三的内容
clr c
mov r7,#08h
lloop: rlc a
mov p0,a
lcall del100ms
djnz r7,lloop
mov r6,#06h
rloop: rrc a
mov p0,a
lcall del100ms
djnz r6,rloop
ret
keyreader: mov a,p1 ;keyreader是键识别子程序,大家可以参考试验7
anl a,#0fh
cjne a,#0dh,pass
lcall del10ms
mov a,p1
anl a,#0fh
cjne a,#0dh,pass
lcall light2 ;如果确定K0按键按下,调用灯光闪烁子程序
ret
light2: mov a,#00h ;light2是让灯光闪烁5次的子程序
mov r5,#10
loop2: mov p0,a
call del10ms
cpl a ;把a寄存器中的数据取反
djnz r5,loop2;
ret
del10ms: mov r4,#15h ;延时10ms子程序
del1: mov r3,#0ffh
del2: djnz r3,del3
djnz r4,del1
ret;
del100ms:mov r2,#
del3: mov r1,#0ffh
del4: djnz r1,del4
djnz r2,del3
ret
end
大家把这个程序下载到学习板上看看,会发现每次按下按键的时候,程序进入中断后,在返回的时候,会回到那个位置继续开始左右循环。这就是由于我们在进入中断的时候保护了现场的缘故。
五、几点说明
主程序是左右跑马灯,其中用到了r7,r6寄存器,还调用了100ms延时,所以也用到了r2,r1寄存器。所以我们要特别注意,在中断服务程序中,要避免使用这几个寄存器。否则,就会导致在中断程序中,修改了r寄存器的内容,导致返回主程序的时候出现问题。
在中断服务程序中,用到了10ms延时程序,这个延时程序使用的寄存器是r4,r3。另外,还调用了light2子程序,其中用到了r5寄存器。所以。主程序和中断服务程序用到的寄存器r就没有冲突。
那么如果由于条件的限制,使得主程序和中断程序的寄存器的数量较多,一组8个寄存器不够,该怎么办呢?
我们也可以象保护a寄存器一样,在进入中断之后,首先把某一个在中断服务程序中也要用到的r寄存器的内容压入堆栈,在退出中断之前再弹出来。
或者我们就重新选择寄存器区吧,由于我们缺省使用的是0区的寄存器组,所以我们就改变psw程序状态字中的rs1和rs0,就可以换另外的一组寄存器区了。例如,我们在进入中断服务程序之后,写这样的两条指令:
clr rs1
setb rs0
这样,我们就用了1区的8个寄存器,这样就没有问题啦。
上一篇:单片机学习之十三:流水灯花样变换(中断)
下一篇:单片机学习之十一:中断方式按键
推荐阅读最新更新时间:2024-03-16 15:09
设计资源 培训 开发板 精华推荐
- 有奖直播:安世半导体先进 SiC MOSFET 助力提升 EV-Charger 和 OBC 应用能效
- 瑞萨电子RL78/G14评估板DIY精彩上演!
- 年末福利!2019 TI 工业应用精选课程汇总,抢楼赢好礼
- 有奖调查 | 您是如何采购或选择电子元器件的?
- 电源小课堂 | 新电气架构激发电动汽车高压系统潜能,答题赢好礼!
- EEWORLD社区月度奖励,赠E金币!
- 安全的革新,全新的验证方式 下载富士通 《频谱验证解决方案 》白皮书 好礼送!
- MPS EMI 知识充电节盛大开启!赚积分赢好礼!
- 罗姆有奖直播|可应用于LiDAR的激光二极管及周边电源推荐
- 干货下载|ADI 公司再生能源—能源储存解决方案