【016】中断的应用中对断点的保护[51]点击数:430发布日期:2006-8-2220:37:00【收藏】【评论】【打印】【编程爱好者论坛】【关闭】实验参考:笨笨工作室-实验八、按键控制跑马灯(中断)。(查看)实验板:FB51A。(查看)实验目的:1掌握堆栈在中断程序中的作用。2掌握让程序保护现场的方法。实验现象:二极管作左右跑马灯,当按下外部按键K1时,8个二极管全部闪烁5次后从K1按下之前的位置继续作跑马灯。在应用中断时,若主程序在正常运行的过程中响应了中断就要转而去执行中断服务程序。那如何在执行完中断服务程序之后完全恢复原来的主程序呢?这就需要在执行中断服务程序之前将相关的状态保护起来,在中断完成后再将这些状态恢复,从而继续执行主程序。51单片机中允许我们从内部RAM中指定一个空间专门保存这些断点处的状态,这个空间就是堆栈。并且还专门给了我们一个8位的堆栈指针,让我们用它来开辟堆栈空间。例如我们给堆栈指针赋值:movsp,#70h,就表示我们把内部数据RAM的地址为70H开始的单元设为堆栈。MCS-51的片内存储器(RAM)共有256字节,高128字节是特殊功能寄存器,地址范围80H~FFH。这一部分可看作系统资源,不能随便利用。而剩下的低128字节区分如下:──┬────────────┐7FHㄧ用户RAM区ㄧㄧ(数据缓冲区、堆栈区)ㄧ30Hㄧ80byteㄧ──┼────────────┤2FHㄧ可位寻址区ㄧ20Hㄧ16byteㄧ──┼────────────┤1FHㄧ第3组工作寄存器区ㄧ18Hㄧ8byteㄧ──┼────────────┤17Hㄧ第2组工作寄存器区ㄧ10Hㄧ8byteㄧ──┼────────────┤0FHㄧ第1组工作寄存器区ㄧ08Hㄧ8byteㄧ──┼────────────┤07Hㄧ第0组工作寄存器区ㄧ00Hㄧ8byteㄧ──┴────────────┘在这低128字节中,工作寄存器区和位寻址区的地址已分配好,我们可以利用的只有30H~7FH的数据缓冲区。所以我们的堆栈指针只能设在这个区域。在主程序中,让程序作左右跑马灯(参考【004】流水灯实验),。程序中通过把寄存器a中的数进行左环移来实现的。而a又是最常用的一个寄存器,在中断服务程序中也多数会用到,所以在响应中断时要将其保存起来(压入堆栈)。由于程序状态字寄存器PSW(位于特殊功能寄存器区)的不同位包含了程序运行状态的不同信息,所以进入中断时也要将PSW的值保护起来。在返回主程序之前,再把它们取出来,这样就可以使得程序从进入中断之前的位置开始,继续作跑马灯。键识别部分参考【015】中断方式按键一文。所用电路如下:显示部分:键盘部分:程序如下:org0000hljmpstartorg0013hljmpext1org0020hstart:setbea;CPU开中断setbex1;允许外部中断1申请中断setbit1;设置外部中断1为跳变方式触发movsp,#70h;设置堆栈入口loop1:lcallled_flow;调用左右流水灯程序ljmploop1ext1:;中断服务程序clrea;CPU关中断pushacc;a入栈pushpsw;psw入栈lcallkey;调用键识别子程序pass:poppsw;恢复现场(与入栈顺序相反)popaccsetbea;CPU开中断reti;中断返回led_flow:mova,#0ffh;左右流水灯子程序clrc;清Cy进位标志位movr7,#08h;左循环次数lloop:rlca;a循环左移movp0,a;a送P0口lcalldel100ms;延时100msdjnzr7,lloop;左移8次movr6,#06h;右循环次数rloop:rrcamovp0,alcalldel100msdjnzr6,rloopretkey:mova,p1;键识别子程序anla,#0fhcjnea,#0dh,passlcalldel10msmova,p1anla,#0fhcjnea,#0dh,passlcallkey_flash;K1按下则调用灯闪程序retkey_flash:mova,#00h;灯闪5次子程序movr5,#10;闪一次有一亮一灭loop2:movp0,acalldel100mscpla;取反djnzr5,loop2retdel10ms:;10ms延时子程序(12M)movr4,#20;2机器周期temp1:movr3,#248;2机器周期djnzr3,$;2机器周期2+2×248=498djnzr4,temp1;2机器周期2×20=40ret;2+20×498+40=10002即10msdel100ms:movr2,#0c3h;100.036mstemp2:movr1,#0ffh;511usdjnzr1,$djnzr2,temp2retend★实验结果:最终下载到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软件本身对这两个中断源的优先级有所区别?还是软件本身有问题……还是感觉这块板上的键盘结构在这个实验中很别扭,所以还是改为直接用一个开关控制,电路如下:程序如下:org0000hljmpstartorg0003hljmpext1org0030hstart:setbea;CPU开中断setbex0;允许外部中断1申请中断setbit0;设置外部中断1为跳变方式触发movsp,#70h;设置堆栈入口loop1:lcallled_flow;调用左右流水灯程序ljmploop1ext1:;中断服务程序clrea;CPU关中断pushacc;a入栈pushpsw;psw入栈lcallkey_flash;K1按下则调用灯闪程序pass:poppsw;恢复现场(与入栈顺序相反)popaccsetbea;CPU开中断reti;中断返回led_flow:mova,#0ffh;左右流水灯子程序clrc;清Cy进位标志位movr7,#08h;左循环次数lloop:rlca;a循环左移movp0,a;a送P0口lcalldel100ms;延时100msdjnzr7,lloop;左移8次movr6,#06h;右循环次数rloop:rrcamovp0,alcalldel100msdjnzr6,rloopretkey_flash:mova,#00h;灯闪5次子程序movr5,#10;闪一次有一亮一灭loop2:movp0,acalldel100mscpla;取反djnzr5,loop2retdel10ms:;10ms延时子程序(12M)movr4,#20temp1:movr3,#248djnzr3,$djnzr4,temp1retdel100ms:movr2,#0c3h;100.036mstemp2:movr1,#0ffhdjnzr1,$djnzr2,temp2retend引用地址:=17770