立即注册 登录
独闷闷网 返回首页

又一个暑假的个人空间 http://dumenmen.com/?842 [收藏] [复制] [分享] [RSS]

日志

51单片机寄存器组的设置

已有 1200 次阅读2015-3-30 10:54 |个人分类:汇编| 单片机, 寄存器

大家都知道51单片机有的寄存器R0-R7共有四组。最近1年来,我在与新手朋友交流时发现,很多朋友对寄存器组的使用时经常出现问题。虽然这并不是多难的问题,但如果出现错误,也会造成很严重的后果。
    首先介绍一下51的寄存器组:
    通过设置PSW寄存器的第3位和第4位可以任意切换寄存器组。在进入中断前,切换寄存器组,可以方便的保护原寄存器组的数据不被中断里的语句破坏,很方便。
RS1 RS0             字节地址
 0   0   0组寄存器  00H~07H
 0   1   1组寄存器  08H~0FH
 1   0   2组寄存器  10H~17H
 1   1   3组寄存器  18H~1FH
 RS1=PSW.4 RS0=PSW.3

常见错误有三种:
1、为中断函数指定了第0组寄存器 
    C程序: void int0() interrupt 0 using 0
    编译后的汇编如下:
    PUSH  ACC 
    PUSH  B
    PUSH  DPH
    PUSH  DPL
    PUSH  PSW
    MOV   PSW,#0x00  
    。。。。。。
    因为main()函数使用的就是第0组寄存器,中断程序会改变寄存器组的数据。主程序运行时,随时都有可能产生中断,等中断返回主程序时,寄存器R0-R7的值已经被改变了。这是非常严重的错误。而且故障时有时无,错误也是莫明其妙。
2、中断优先级不同,寄存器组号相同
    C程序: void int0()  interrupt  0  using  1  //低优先级中断
             void T0()  interrupt  1  using  1  //高优先级中断 
    因为高优先级的中断可以打断正在执行的低级中断,转向持行高级中断。这就是所谓的中断的中断。与第1种错语一样,高级中断的程序,会改变低级中断正在使用的寄存器。
3、不写using 。严格的说,这样写不能算是错误。但这是相当不好的习惯。
    C程序: void int0()  interrupt  0
    编译后的汇编如下:
    PUSH  ACC 
    PUSH  B
    PUSH  DPH
    PUSH  DPL
    PUSH  PSW
    MOV   PSW,#0x00  
    PUSH  0x00
    PUSH  0x01
    PUSH  0x02
    PUSH  0x03
    PUSH  0x04
    PUSH  0x05
    PUSH  0x06
    PUSH  0x07
    。。。。。。
    没用using指定寄存器组,编译器就默认分配了第0组寄存器,然后又用8条语句把第0组的R0-R7保存到栈中,退出中断时还需要8个弹栈。这样“笨拙”的写法,占用了程序空间32个字节、占有堆栈8个字节。也许高级版本的编译器会改进吧,搞单片机的还是规矩些好。
    
经验总结:
    1、写中断程序一定要用using语句指定寄存器组。第1、2、3组都可以,不能是0.
    2、51单片机的中断有两个优先级。一个中断不会打断另一个相同优先级的中断。这样相同级别中断可以使用同一个组。比如:低优先级的中断函数都用 using 1,高优先级的中断都用 using 2 。这样不会冲突。
    下面是一个正常的例子:
    C程序: void int0() interrupt 0 using 1
    编译后的汇编如下:
    PUSH  ACC 
    PUSH  B
    PUSH  DPH
    PUSH  DPL
    PUSH  PSW
    MOV   PSW,#0x08  
        。。。。。。
    
    就是这么简单。虽然这点事儿对老鸟不算什么问题,但新手朋友犯此错误的可不少。我遇到的就不下15个了。今天我写这个贴子,也是为所有新手提个醒。到处救火不如防患于未燃。

路过

雷人

握手

鲜花

鸡蛋

发表评论 评论 (1 个评论)

回复 又一个暑假 2015-3-30 10:58
用上的好处多

用上using可以精简代码,节省堆栈,不过有时会出现一个问题:
用上using ,在中断服务程序里调用函数要小心一点,因为keil C有时会产生依赖绝对地址的代码,例如如下函数,功能是从片外的存储设备中读取一个字节:
uchar ReadByte(uchar address)
{
  retrun PBYTE[address];
}
会被编译成如下代码:
  MOV      R0,0x07
  MOVX     A,@R0
  MOV      R7,A
这时,如果在中断服务程序里调用 ReadByte(0xAA); 就会发现读出的数据根本不对,因为using 1使得中断服务程序在调用函数时使用第一组寄存器传递参数,编译器生成的代码如下:
  MOV      R7,#0xAA
  LCALL    ReadByte
而ReadByte这个函数的代码是使用绝对地址为0x07的第0组寄存器的R7来传递参数的,所以会出问题。
解决方法是在定义ReadByte这个函数的前面加上"#pragma noaregs",这样编译器就会生成不依赖于绝对地址的代码了,函数ReadByte被编译生成的代码如下:
XCH      A,R0
MOV      A,R7
XCH      A,R0
MOVX     A,@R0
MOV      R7,A
这样就可以大胆的使用using了,使用using才是充分利用51架构的使用方法。

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 立即注册

QQ|Archiver|手机版|独闷闷网 ( 粤ICP备12007667号-2 )

GMT+8, 2024-4-20 04:01 , Processed in 0.125781 second(s), 21 queries .

返回顶部