正文

【016】中断的应用中对断点的保护 [51]2006-08-22 20:37:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/wentao/17770.html

分享到:

实验参考: 笨笨工作室-实验八、按键控制跑马灯(中断)。(查看)
实验板:   FB51A。(查看)

实验目的: <1> 掌握堆栈在中断程序中的作用。
     <2> 掌握让程序保护现场的方法。

实验现象: 二极管作左右跑马灯,当按下外部按键 K1 时, 8 个二极管全部闪烁 5 次后从 K1 按下之前的位置继续作跑马灯。


在应用中断时,若主程序在正常运行的过程中响应了中断就要转而去执行中断服务程序。那如何在执行完中断服务程序之后完全恢复原来的主程序呢?这就需要在执行中断服务程序之前将相关的状态保护起来,在中断完成后再将这些状态恢复,从而继续执行主程序。

51单片机中允许我们从内部 RAM 中指定一个空间专门保存这些断点处的状态,这个空间就是堆栈。并且还专门给了我们一个 8 位的堆栈指针,让我们用它来开辟堆栈空间。例如我们给堆栈指针赋值: mov sp, #70h ,就表示我们把内部数据 RAM 的地址为 70H 开始的单元设为堆栈。

MCS-51的片内存储器(RAM)共有256字节,高128字节是特殊功能寄存器,地址范围80H~FFH。这一部分可看作系统资源,不能随便利用。而剩下的低128字节区分如下:

──┬────────────┐
 7FHㄧ       用户RAM区        ㄧ
    ㄧ (数据缓冲区、堆栈区) ㄧ
 30Hㄧ        80 byte         ㄧ
──┼────────────┤
 2FHㄧ      可位寻址区        ㄧ
 20Hㄧ        16 byte         ㄧ
──┼────────────┤
 1FHㄧ   第3组工作寄存器区    ㄧ
 18Hㄧ         8 byte         ㄧ
──┼────────────┤
 17Hㄧ   第2组工作寄存器区    ㄧ
 10Hㄧ         8 byte         ㄧ
──┼────────────┤
 0FHㄧ   第1组工作寄存器区    ㄧ
 08Hㄧ         8 byte         ㄧ
──┼────────────┤
 07Hㄧ   第0组工作寄存器区    ㄧ
 00Hㄧ         8 byte         ㄧ
──┴────────────┘

在这低128字节中,工作寄存器区和位寻址区的地址已分配好,我们可以利用的只有 30H ~ 7FH 的数据缓冲区。所以我们的堆栈指针只能设在这个区域。

在主程序中,让程序作左右跑马灯(参考【004】流水灯实验),。程序中通过把寄存器a中的数进行左环移来实现的。而a又是最常用的一个寄存器,在中断服务程序中也多数会用到,所以在响应中断时要将其保存起来(压入堆栈)。由于程序状态字寄存器PSW(位于特殊功能寄存器区)的不同位包含了程序运行状态的不同信息,所以进入中断时也要将PSW的值保护起来。在返回主程序之前,再把它们取出来,这样就可以使得程序从进入中断之前的位置开始,继续作跑马灯。

键识别部分参考【015】中断方式按键一文。


所用电路如下:

显示部分:


键盘部分:




程序如下:

          org  0000h   
          ljmp start
          org  0013h
          ljmp ext1
        
          org  0020h
start:    setb ea         ; CPU开中断
          setb ex1        ; 允许外部中断1申请中断
          setb it1        ; 设置外部中断1为跳变方式触发
          mov  sp, #70h   ; 设置堆栈入口
        
loop1:    lcall led_flow  ; 调用左右流水灯程序
          ljmp  loop1
        

ext1:                     ; 中断服务程序
          clr   ea        ; CPU关中断
          push  acc       ; a 入栈
          push  psw       ; psw 入栈

          lcall key       ; 调用键识别子程序
pass:     pop   psw       ; 恢复现场(与入栈顺序相反)
          pop   acc

          setb  ea        ; CPU开中断
          reti            ; 中断返回
        
led_flow: mov   a, #0ffh  ; 左右流水灯子程序
          clr   c         ; 清Cy进位标志位
          mov   r7, #08h  ; 左循环次数
lloop:    rlc   a         ; a循环左移
          mov   p0, a     ; a送P0口
          lcall del100ms  ; 延时100ms
          djnz  r7, lloop ; 左移8次
         
          mov   r6, #06h  ; 右循环次数
rloop:    rrc   a
          mov   p0, a
          lcall del100ms
          djnz  r6, rloop
          ret
         
key:       mov   a, p1         ; 键识别子程序
           anl   a, #0fh
           cjne  a, #0dh, pass
           lcall del10ms
           mov   a, p1
           anl   a, #0fh
           cjne  a, #0dh, pass
           lcall key_flash     ; K1按下则调用灯闪程序
           ret
          
key_flash: mov   a, #00h       ; 灯闪5次子程序
           mov   r5, #10       ; 闪一次有一亮一灭
loop2:     mov   p0, a
           call  del100ms
           cpl   a             ; 取反
           djnz  r5, loop2
           ret
          
del10ms:                      ; 10ms延时子程序(12M)
           mov  r4, #20       ; 2机器周期
temp1:     mov  r3, #248      ; 2机器周期
           djnz r3, $         ; 2机器周期 2+2×248=498
           djnz r4, temp1     ; 2机器周期 2×20=40
           ret                ; 2+20×498+40=10002 即10ms 
        
del100ms:  mov  r2, #0c3h     ;100.036ms
temp2:     mov  r1, #0ffh     ;511us
           djnz r1, $
           djnz r2, temp2
           ret  
   
end   



实验结果: 最终下载到FB51A实验板上得到预计结果。但在用Porteus仿真时却出现问题,流水灯正常运行时按下K1键后,8只LED闪烁5次后所有LED全灭,并未继续运行。在LED正常"流水"时按下K0、K2、或K3键时,则停在当前状态(一只LED亮)。经过调试后仍未解决,由于这个板上的键盘设计的有些不同,每个键都通过一个4148连到P3.3,而在Proteus中仿真时用4148行不通,所以用4001代替的。所以怀疑是这里的问题, 于是又试着用另一块板(AS系统)试了一下, 将外部中断1(P3.3)直接接一个开关后接地, 这样就可以直接触发中断了, 程序中去除了键识别的部分, 试验结果还是一样, 板上实测通过,仿真仍然会停在中断服务程序中。

后来又试着换了个中断源,原来用的外部中断1,现在改用外部中断0,程序中作相应的修改后再仿真就OK了。又翻了翻书,外部中断1和外部中断0好像没什么不同,只是外部中断0的优先级要比外部中断1高。而在板上实测二者均通过,只是在仿真的时候出现问题,难道是Porteus软件本身对这两个中断源的优先级有所区别? 还是软件本身有问题……


还是感觉这块板上的键盘结构在这个实验中很别扭,所以还是改为直接用一个开关控制,电路如下:


程序如下:

          org  0000h
          ljmp start
          org  0003h
          ljmp ext1
        
          org  0030h
start:    setb ea         ; CPU开中断
          setb ex0        ; 允许外部中断1申请中断
          setb it0        ; 设置外部中断1为跳变方式触发
          mov  sp, #70h   ; 设置堆栈入口
        
loop1:    lcall led_flow  ; 调用左右流水灯程序
          ljmp  loop1
        

ext1:                     ; 中断服务程序
          clr   ea        ; CPU关中断
          push  acc       ; a 入栈
          push  psw       ; psw 入栈
          lcall key_flash ; K1按下则调用灯闪程序
pass:     pop   psw       ; 恢复现场(与入栈顺序相反)
          pop   acc
          setb  ea        ; CPU开中断   
          reti            ; 中断返回
        
led_flow: mov   a, #0ffh  ; 左右流水灯子程序
          clr   c         ; 清Cy进位标志位
          mov   r7, #08h  ; 左循环次数
lloop:    rlc   a         ; a循环左移
          mov   p0, a     ; a送P0口
          lcall del100ms  ; 延时100ms
          djnz  r7, lloop ; 左移8次
         
          mov   r6, #06h  ; 右循环次数
rloop:    rrc   a
          mov   p0, a
          lcall del100ms
          djnz  r6, rloop
          ret
          
key_flash: mov   a, #00h       ; 灯闪5次子程序
           mov   r5, #10       ; 闪一次有一亮一灭
loop2:     mov   p0, a
           call  del100ms
           cpl   a             ; 取反
           djnz  r5, loop2
           ret
          
del10ms:                      ; 10ms延时子程序(12M)
           mov  r4, #20
temp1:     mov  r3, #248
           djnz r3, $
           djnz r4, temp1
           ret
        
del100ms:  mov  r2, #0c3h     ;100.036ms
temp2:     mov  r1, #0ffh
           djnz r1, $
           djnz r2, temp2
           ret  
   
end   

 

阅读(5079) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册