51的堆栈

51单片机堆栈  

2012-04-03 21:40:31|  分类: 单片机|字号 订阅

        堆栈是一种数据结构。一直以为堆栈是一个寄存器,惭愧!教科书定义:所谓堆栈,就是只允许在其一端进行数据插入和数据删除的线性表。51单片机的单片机的堆栈是在内部RAM中开辟的。这句话表明了堆栈的位置。

        那么堆栈到底有什么作用?

堆栈主要是为子程序调用和中断操作而设立的,因此对应有两项功能:保护断点和保护现场。

单片机的程序归根结底是个死循环,反复在执行Main函数(主程序),你可以只写一个函数Main,那么你这个函数随着功能的增多而变得异常大,而且非常不具备可读,这个时候就需要子函数(子程序)了。主函数在调用完子函数后会返回到主函数中,这样就可以调用其它函数并且继续这个死循环。在计算机去执行子函数或者中断服务函数,如何确保程序能够正确地返回到主函数中并且继续正确执行后面的内容?因为在执行子函数或者中断服务函数时,很有可能会破坏寄存器单元的内容,但这些寄存器单元在子函数必须要用到?这个问题看起来比较难解决了。这个时候就要用到 断点保护和现场保护了。

        保护断点:在调用子程序和堆栈时,将返回地址(执行完子程序或者中断后要执行的下一个指令的的地址(PC寄存器值)) 送入堆栈,程序返回时,这个值自动弹回PC。这种方式是自动使用堆栈的,程序中一般无需理会。在这个过程中,地址送入堆栈时,堆栈指针SP+2,因为51的寻址范围是64KB,再查看RAM中堆栈单元就可以发现这个时候已经变成了PC的值,在返回的时候SP-2,但是堆栈中的内容在下一次堆栈操作之前不会发生变化。来看下面的仿真图:

51单片机堆栈 - dzdesigned80 - dzdesigned80的博客

 程序初始化将SP设置为0x07,这个时候RAM中全部被清零。

 

单步运行。

51单片机堆栈 - dzdesigned80 - dzdesigned80的博客

 
这个时候SP的值已经变为0x30,但是内容依旧没有改变。

运行到断点。

51单片机堆栈 - dzdesigned80 - dzdesigned80的博客

  运行到断点PC的值没什么变化,在执行完Lcall之后,发现RAM中30单元起了变化,0013,PC也+2,而0013就是sjmp st1的地址。

以上是用Protues仿真的结果。在用KEIL做软仿的时候发现SP会变化,但是对应的内容却没有变化,这个是不是KEIL的一个BUG呢?

 

         保护断点是比较简单的,编程者一般不用理会。现场保护就和编程扯上关系了。现场保护:在转中断服务程序或者子程序之前,要把单片机中个有关单元的内容保存起来,这就是所谓的现场保护。有保护当然就有恢复。现场恢复:返回主程序之后恢复寄存器的内容到调用之前的状态。

 这里就首先要牵涉一个using的用法了。

写过中断服务函数的人都知道,中断服务函数一般都是 这种形式的

void isr(void) interrupt x (using x) //using x是可选的

{

}

教单片机的老师当时说你搞不清using 的含义,写中断服务程序不用用它就万事大吉了。这个说法不能说是没有道理的。很长一段时间我写中断服务程序也是不管这个,直到有一次中断RAM溢出,才感到这个using用好了还是有点用处的。 “8051是一个基于累加器的单片机,具有8个通用寄存器,每个寄存器都是一个单字节的寄存器。这8个寄存器通用寄存器可以认为是一组寄存器或者一个通用寄存器组。8051提供四组可用的寄存器组。当使用中断时,多组寄存器切换将带来许多方便。典型8051 C程序不需要选择或者切换寄存器组,默认使用寄存器组0。寄存器组1·2·3在中断服务程序中使用。”引自北航版《单片机的C语言应用程序设计》
来看具体的程序。
不实用using,默认使用寄存器组0

void Timer0(void) interrupt 1 {  TH0 = TH0_VAL;    TL0 = TL0_VAL;  time_1ms_counter++;  if(0==(time_1ms_counter%10))   {    ScanCardFlag=1;    LCMDisplayAndScanKey();//显示时间   }   if(64==time_1ms_counter)   {    time_1ms_counter=1;   }  }

 

对应的汇编语言程序

                  Timer0: C:0x9D70    C0E0     PUSH     ACC(0xE0) C:0x9D72    C0F0     PUSH     B(0xF0) C:0x9D74    C083     PUSH     DPH(0x83) C:0x9D76    C082     PUSH     DPL(0x82) C:0x9D78    C0D0     PUSH     PSW(0xD0)

C:0x9D7A    75D000   MOV      PSW(0xD0),#os_buf(0x00) ;选择寄存器组0 C:0x9D7D    C000     PUSH     os_buf(0x00) C:0x9D7F    C001     PUSH     0x01 C:0x9D81    C002     PUSH     0x02 C:0x9D83    C003     PUSH     0x03 C:0x9D85    C004     PUSH     0x04 C:0x9D87    C005     PUSH     0x05 C:0x9D89    C006     PUSH     0x06 C:0x9D8B    C007     PUSH     0x07 C:0x9D8D    758CB7   MOV      TH0(0x8C),#IPH0(0xB7) C:0x9D90    758AFF   MOV      TL0(0x8A),#0xFF C:0x9D93    90007E   MOV      DPTR,#time_1ms_counter(0x007E) C:0x9D96    E0       MOVX     A,@DPTR C:0x9D97    04       INC      A C:0x9D98    F0       MOVX     @DPTR,A C:0x9D99    E0       MOVX     A,@DPTR C:0x9D9A    75F00A   MOV      B(0xF0),#0x0A C:0x9D9D    84       DIV      AB C:0x9D9E    E5F0     MOV      A,B(0xF0) C:0x9DA0    7005     JNZ      C:9DA7 C:0x9DA2    D208     SETB     ScanCardFlag(0x21.0) C:0x9DA4    12905C   LCALL    LCMDisplayAndScanKey(C:905C) C:0x9DA7    90007E   MOV      DPTR,#time_1ms_counter(0x007E) C:0x9DAA    E0       MOVX     A,@DPTR C:0x9DAB    B44003   CJNE     A,#0x40,C:9DB1 C:0x9DAE    7401     MOV      A,#0x01 C:0x9DB0    F0       MOVX     @DPTR,A C:0x9DB1    D007     POP      0x07 C:0x9DB3    D006     POP      0x06 C:0x9DB5    D005     POP      0x05 C:0x9DB7    D004     POP      0x04 C:0x9DB9    D003     POP      0x03 C:0x9DBB    D002     POP      0x02 C:0x9DBD    D001     POP      0x01 C:0x9DBF    D000     POP      os_buf(0x00) C:0x9DC1    D0D0     POP      PSW(0xD0) C:0x9DC3    D082     POP      DPL(0x82) C:0x9DC5    D083     POP      DPH(0x83) C:0x9DC7    D0F0     POP      B(0xF0) C:0x9DC9    D0E0     POP      ACC(0xE0) C:0x9DCB    32       RETI    

使用using 1


void Timer0(void) interrupt 1 using 1 {  TH0 = TH0_VAL;       TL0 = TL0_VAL;  time_1ms_counter++;  if(0==(time_1ms_counter%10))   {    ScanCardFlag=1;    LCMDisplayAndScanKey();//显示时间   }   if(64==time_1ms_counter)   {    time_1ms_counter=1;   }    }

 

对应的汇编语言程序

                 Timer0: C:0xA131    C0E0     PUSH     ACC(0xE0) C:0xA133    C0F0     PUSH     B(0xF0) C:0xA135    C083     PUSH     DPH(0x83) C:0xA137    C082     PUSH     DPL(0x82) C:0xA139    C0D0     PUSH     PSW(0xD0) C:0xA13B    75D008   MOV      PSW(0xD0),#0x08 ;选择寄存器组1 C:0xA13E    758CB7   MOV      TH0(0x8C),#IPH0(0xB7) C:0xA141    758AFF   MOV      TL0(0x8A),#0xFF C:0xA144    90007E   MOV      DPTR,#time_1ms_counter(0x007E) C:0xA147    E0       MOVX     A,@DPTR C:0xA148    04       INC      A C:0xA149    F0       MOVX     @DPTR,A C:0xA14A    E0       MOVX     A,@DPTR C:0xA14B    75F00A   MOV      B(0xF0),#0x0A C:0xA14E    84       DIV      AB C:0xA14F    E5F0     MOV      A,B(0xF0) C:0xA151    7005     JNZ      C:A158 C:0xA153    D208     SETB     ScanCardFlag(0x21.0) C:0xA155    12905C   LCALL    LCMDisplayAndScanKey(C:905C) C:0xA158    90007E   MOV      DPTR,#time_1ms_counter(0x007E) C:0xA15B    E0       MOVX     A,@DPTR C:0xA15C    B44003   CJNE     A,#0x40,C:A162 C:0xA15F    7401     MOV      A,#0x01 C:0xA161    F0       MOVX     @DPTR,A C:0xA162    D0D0     POP      PSW(0xD0) C:0xA164    D082     POP      DPL(0x82) C:0xA166    D083     POP      DPH(0x83) C:0xA168    D0F0     POP      B(0xF0) C:0xA16A    D0E0     POP      ACC(0xE0)

转载于:https://my.oschina.net/u/1024767/blog/148299

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值