×
单片机 > 单片机程序设计 > 详情

单片机学习之十二:按键控制跑马灯(中断)

发布时间:2020-06-19 发布时间:
|
一、实验现象:

 

     二极管作左右跑马灯,当按下外部按键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,#0c8h ;延时100ms子程序

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个寄存器,这样就没有问题啦。

关键字:单片机  按键控制  跑马灯  中断

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

热门文章 更多
8051单片机的函数发生器的设计